小程序对接腾讯云IM即时聊天

417 阅读11分钟

小程序对接腾讯云IM即时聊天

步骤如下

  • 下载sdk并引入(tim-wx-sdk、cos-wx-sdk-v5)
  • 建立监听(Event.js)
  • 初始化IM
  • 登录IM
  • 监听IM各个状态钩子
  • 聊天(获取聊天记录、发送消息、接受消息、发送图片、发送自定义消息)

一:下载sdk

一:下载sdk

(1)npm方式下载

	// IM 小程序 SDK
	npm install tim-wx-sdk --save
	// 发送图片、文件等消息需要的 COS SDK
	npm install cos-wx-sdk-v5 --save

(2)直接下载js文件

     下载链接(https://cloud.tencent.com/document/product/269/36887)
     或(文章最后会提供完整代码)

二:初始化IM

  • 开发过程遇到的其中一个问题就是数据同步,应该是有很多方法的,这儿采用的是引入一个监听器,在app.js里面,将Event监听器挂载到wx对象上
  • 初始化im
  • 登录im
//app.js

import Event from './plugin/tencentIM/event'
wx.event = new Event();

import {initTIM,loginTIM}  from './plugin/im-init'

App({

  require ($uri){
    return require($uri)
  },

  onLaunch() {
    initTIM(this.globalData);//初始化IM
    loginTIM(this.globalData);//登录IM
  },

  globalData: {
    TIM: null,
    TIM_INIT:false
  }

});


//im-init.js

import TIM from "tim-wx-sdk";//npm方式引入
import COS from "cos-wx-sdk-v5";//npm方式引入

let DATA_APP = getApp() && getApp().globalData || {};
let DATA_TIM = DATA_APP.TIM || {};


/**
 * IM初始化
 * @return null
 */
export function initTIM(item) {
    let options = {
        SDKAppID: '这里填写你自己SDKAppID'
    };
    if(item){
        DATA_APP = item;
    }
    //避免重复创建im实例
    if (DATA_APP.TIM_INIT) {
        return
    }
    let DATA_TIM_READY = false;
    // 创建 SDK 实例,`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
    DATA_TIM = TIM.create(options);// SDK 实例通常用 DATA_TIM 表示
    // 设置 SDK 日志输出级别,详细分级请参见 setLogLevel 接口的说明
    DATA_TIM.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
    // DATA_TIM.setLogLevel(1); // release 级别,SDK 输出关键信息,生产环境时建议使用
    // 注册 COS SDK 插件   此处暂时隐藏有需求要传图片,文件等的请放开进行配置,记住头部引入
    DATA_TIM.registerPlugin({'cos-wx-sdk': COS});
    // 监听事件,例如:
    DATA_TIM.on(TIM.EVENT.SDK_READY, function (event) {
        //此回调函数中发布'conversationInit'对话列表初始化事件触发页面js中的
        //需要注意的是DATA_TIM.getMessageList({})这个方法必须要在SDK_READY状态才能调用,所以要放在这个回调函数中
        wx.event.emit('conversationList');// 会话列表的监听函数
        wx.event.emit('conversationInit');// 聊天记录的监听函数
        DATA_TIM_READY = true;
        // 收到离线消息和会话列表同步完毕通知,接入侧可以调用 sendMessage 等需要鉴权的接口
        // event.name - TIM.EVENT.SDK_READY
    });
    DATA_TIM.on(TIM.EVENT.MESSAGE_RECEIVED, function (event) {
        console.log('收到消息');
        // 这里收到消息,调用setGlobalMsg方法来处理数据,传入方式标记为'received'接收消息
        wx.event.emit('received', event) // 会话列表的监听函数
        // setGlobalMsg(event,'received');
        // 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
        // event.name - TIM.EVENT.MESSAGE_RECEIVED
        // event.data - 存储 Message 对象的数组 - [Message]
    });
    DATA_TIM.on(TIM.EVENT.MESSAGE_REVOKED, function (event) {
        // 收到消息被撤回的通知
        // event.name - TIM.EVENT.MESSAGE_REVOKED
        // event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isRevoked 属性值为 true
    });
    DATA_TIM.on(TIM.EVENT.MESSAGE_READ_BY_PEER, function (event) {
        // SDK 收到对端已读消息的通知,即已读回执。使用前需要将 SDK 版本升级至 v2.7.0 或以上。仅支持单聊会话。
        // event.name - TIM.EVENT.MESSAGE_READ_BY_PEER
        // event.data - event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isPeerRead 属性值为 true
    });
    DATA_TIM.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function (event) {
        // 收到会话列表更新通知,可通过遍历 event.data 获取会话列表数据并渲染到页面
        // event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
        // event.data - 存储 Conversation 对象的数组 - [Conversation]
        if(DATA_TIM_READY){
            wx.event.emit('conversationList') // 会话列表的监听函数
        }
    });
    DATA_TIM.on(TIM.EVENT.GROUP_LIST_UPDATED, function (event) {
        // 收到群组列表更新通知,可通过遍历 event.data 获取群组列表数据并渲染到页面
        // event.name - TIM.EVENT.GROUP_LIST_UPDATED
        // event.data - 存储 Group 对象的数组 - [Group]
    });
    DATA_TIM.on(TIM.EVENT.PROFILE_UPDATED, function (event) {
        // 收到自己或好友的资料变更通知
        // event.name - TIM.EVENT.PROFILE_UPDATED
        // event.data - 存储 Profile 对象的数组 - [Profile]
    });
    DATA_TIM.on(TIM.EVENT.BLACKLIST_UPDATED, function (event) {
        // 收到黑名单列表更新通知
        // event.name - TIM.EVENT.BLACKLIST_UPDATED
        // event.data - 存储 userID 的数组 - [userID]
    });
    DATA_TIM.on(TIM.EVENT.ERROR, function (event) {
        // 收到 SDK 发生错误通知,可以获取错误码和错误信息
        // event.name - TIM.EVENT.ERROR
        // event.data.code - 错误码
        // event.data.message - 错误信息
    });
    DATA_TIM.on(TIM.EVENT.SDK_NOT_READY, function (event) {
        // 收到 SDK 进入 not ready 状态通知,此时 SDK 无法正常工作
        // event.name - TIM.EVENT.SDK_NOT_READY
    });
    DATA_TIM.on(TIM.EVENT.KICKED_OUT, function (event) {
        // 收到被踢下线通知
        // event.name - TIM.EVENT.KICKED_OUT
        // event.data.type - 被踢下线的原因,例如:
        //    - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多实例登录被踢
        //    - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多终端登录被踢
        //    - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 签名过期被踢 (v2.4.0起支持)。
    });
    DATA_TIM.on(TIM.EVENT.NET_STATE_CHANGE, function (event) {
        //  网络状态发生改变(v2.5.0 起支持)。
        // event.name - TIM.EVENT.NET_STATE_CHANGE
        // event.data.state 当前网络状态,枚举值及说明如下:
        //     \- TIM.TYPES.NET_STATE_CONNECTED - 已接入网络
        //     \- TIM.TYPES.NET_STATE_CONNECTING - 连接中。很可能遇到网络抖动,SDK 在重试。接入侧可根据此状态提示“当前网络不稳定”或“连接中”
        //    \- TIM.TYPES.NET_STATE_DISCONNECTED - 未接入网络。接入侧可根据此状态提示“当前网络不可用”。SDK 仍会继续重试,若用户网络恢复,SDK 会自动同步消息
    });
    DATA_APP.TIM_INIT = true;  //完成im实例创建后设置标志为true
    DATA_APP.TIM = DATA_TIM;
    console.log(DATA_APP)
}

/**
 * IM登录
 * @param {Object} item 数据
 * @param {String} item.IM_ID
 * @param {Date} item.IM_KEY
 * @return null
 */
export function loginTIM(item) {
    if(item){
        DATA_TIM = item.TIM;
    }
    DATA_TIM.login({
        userID: '从后端获取的IM userID',
        userSig: '从后端获取的IM userSig'
    }).then((imResponse) => {
        console.log('IM登录成功:', imResponse);
    }).catch((imError) => {
        console.log('IM登录失败:', imError);
    });
}

/**
 * IM退出
 * @return null
 */
export function loginOutTIM(item) {
    if(item){
        DATA_APP = item;
    }
    DATA_TIM.logout().then((imResponse) => {
        console.log('IM退出成功:', imError);
    }).catch((imError) => {
        console.log('IM退出失败:', imError);
    });
}

//Event.js

class Event {
    /**
     * on 方法把订阅者所想要订阅的事件及相应的回调函数记录在 Event 对象的 _cbs 属性中
     */
    on(event, fn) {
        if (typeof fn != "function") {
            console.error('fn must be a function')
            return
        }
        this._cbs = this._cbs || {};
        (this._cbs[event] = this._cbs[event] || []).push(fn)
    }
    /**
     * emit 方法接受一个事件名称参数,在 Event 对象的 _cbs 属性中取出对应的数组,并逐个执行里面的回调函数
     */
    emit(event) {
        this._cbs = this._cbs || {}
        var callbacks = this._cbs[event], args
        if (callbacks) {
            callbacks = callbacks.slice(0)
            args = [].slice.call(arguments, 1)
            for (var i = 0, len = callbacks.length; i < len; i++) {
                callbacks[i].apply(null, args)
            }
        }
    }
    /**
     * off 方法接受事件名称和当初注册的回调函数作参数,在 Event 对象的 _cbs 属性中删除对应的回调函数。
     */
    off(event, fn) {
        this._cbs = this._cbs || {}
        // all
        if (!arguments.length) {
            this._cbs = {}
            return
        }
        var callbacks = this._cbs[event]
        if (!callbacks) return
        // remove all handlers
        if (arguments.length === 1) {
            delete this._cbs[event]
            return
        }
        // remove specific handler
        var cb
        for (var i = 0, len = callbacks.length; i < len; i++) {
            cb = callbacks[i]
            if (cb === fn || cb.fn === fn) {
                callbacks.splice(i, 1)
                break
            }
        }
        return
    }
}
export default Event

三:一对一聊天

(1)index.wxml(图片部分自行补充)


<view class="chat-area">
    <scroll-view scroll-into-view="{{ colToView }}" scroll-y="true" bindscrolltoupper="onLoadMore" bindtap="onHideSendMore">
        <view class="loader-wrap" wx:if="{{showLoading}}" >
            <text class="loader"></text>
        </view>
        <block wx:for="{{ arrMsg }}" wx:key="index">
            <view class="{{ item.from === colUserId?'':'chat-his'}}">
                <view wx:if="{{item.type == 'TIMImageElem'}}" class="chat-row flex-box-end" id="msg-{{index}}">
                    <view class="chat-img">
                        <image wx:if="{{item.status=='success'}}" class="chat-pic" mode="heightFix" lazy-load="{{true}}" src="{{item.payload.imageInfoArray[0].url}}" bindtap="previewImage" data-url="{{item.payload.imageInfoArray[2].url}}"></image>
                        <image wx:elif="{{item.status=='fail'}}" class="chat-pic" mode="heightFix" lazy-load="{{true}}" src="{{item.sendPic}}" data-url="{{item.payload.imageInfoArray[2].url}}">
                            <view class="chat-progress">发送失败</view>
                        </image>
                        <image wx:else class="chat-pic" mode="heightFix" lazy-load="{{true}}" src="{{item.sendPic}}" data-url="{{item.payload.imageInfoArray[2].url}}">
                            <view class="chat-progress">
                                <text class="loader"></text>
                                <text>{{colPercent}}%</text>
                            </view>
                        </image>
                    </view>
                    <view class="chat-head">
                        <image class="chat-headPortrait" src="/images/pic_1.png"></image>
                    </view>
                </view>
                <view wx:elif="{{item.type == 'TIMCustomElem'}}" class="chat-row flex-box-end" id="msg-{{index}}">
                    <view class="chat-main">
                        <view class="chat-msg chat-custom">
                            <view class="row">{{item.dataCustom.name}}就诊信息</view>
                            <view class="row">
                                <text class="item">{{item.dataCustom.sex}}</text>
                                <text class="item">{{item.dataCustom.age}}</text>
                                <text class="item">{{item.dataCustom.height}}</text>
                                <text class="item">{{item.dataCustom.weight}}</text>
                            </view>
                            <view class="row">
                                <view class="row-left">病情描述:</view>
                                <view class="row-right">{{item.dataCustom.desc}}</view>
                            </view>
                            <view class="row">
                                <view class="row-left">患病时长:</view>
                                <view class="row-right">{{item.dataCustom.descTime||'未知'}}</view>
                            </view>
                            <view class="row">
                                <view class="row-left">病史:</view>
                                <view class="row-right">{{item.dataCustom.history||'暂无'}}</view>
                            </view>
                            <view class="row">
                                <view class="row-left">过敏史:</view>
                                <view class="row-right">{{item.dataCustom.allergy||'无'}}</view>
                            </view>
                        </view>
                        <view class="chat-time">{{ item.timeFormat }}</view>
                    </view>
                    <view class="chat-head">
                        <image class="chat-headPortrait" src="/images/pic_1.png"></image>
                    </view>
                </view>
                <view wx:else class="chat-row flex-box-end" id="msg-{{index}}">
                    <view class="chat-main">
                        <!--  <view class="left-chat-name">{{ item.from }}</view>-->
                        <view class="chat-msg">{{ item.payload.text }}</view>
                        <view class="chat-time">{{ item.timeFormat }}</view>
                    </view>
                    <view class="chat-head">
                        <image class="chat-headPortrait" src="/images/pic_1.png"></image>
                    </view>
                </view>
            </view>
        </block>
    </scroll-view>
    <view class="chat-footer">
        <view class="chat-input-box">
            <input bindinput='onInputMsg' bindconfirm='TIM_createMsg' value="{{colSendMsg}}" placeholder='输入内容' class="input" cursor-spacing="20"></input>
            <view class='footer-send' bindtap='onShowSendMore'>
                更多
            </view>
        </view>
        <view class="open-more" wx:if="{{showSendMore}}">
            <view class="open-more-item" bindtap="TIM_createPhoto" data-name="album">
                <image src="/images/im-img.png" mode="widthFix" class="msg-img"></image>
                <view class="msg-text">相册</view>
            </view>
            <view class="open-more-item" bindtap="TIM_createPhoto" data-name="camera">
                <image src="/images/im-photo.png" class="msg-img" mode="widthFix"></image>
                <view class="msg-text">拍摄</view>
            </view>
            <view class="open-more-item" bindtap="TIM_createMsgCustom" data-name="camera">
                <image src="/images/im-edit.png" class="msg-img" mode="widthFix"></image>
                <view class="msg-text">自定义消息</view>
            </view>
        </view>
    </view>
</view>

(2)index.wxss


view,text,image{
  box-sizing: border-box;
}

.flex-box-start{
    display: flex;
    justify-content: start;
}

.flex-box-end{
    display: flex;
    justify-content: flex-end;
}

.chat-area{
    width: 100%;
    height: calc(100vh - 120rpx);
    background-color: #f9f9f9;
    font-weight: 400;
    /*padding-bottom: 140rpx;*/
}

.chat-area > scroll-view{
    position: relative;
    height: 100%;
    padding: 20rpx 20rpx 10rpx 20rpx;
    box-sizing: border-box;
}

.chat-his{

}

.chat-his .flex-box-end{
    flex-direction: row-reverse;
    justify-content: flex-start!important;
}

.chat-his .chat-main{
    align-items: flex-start!important;
}

.chat-his .chat-msg{
    background: #ffffff!important;
    color: #000000;
}

.chat-his .chat-msg:before{
    content: '';
    position: absolute;
    width: 0;
    height: 0;
    border-width: 14rpx 20rpx;
    border-style: solid;
    border-color: transparent white transparent transparent;
    left: -40rpx;
    top: 30rpx;
}

.chat-his .chat-msg:after{
    display: none;
}

.chat-his .chat-head{
    margin-right: 30rpx;
    margin-left: 0!important;
}

.chat-his .chat-img{
    justify-content: flex-start!important;
}

.chat-his .chat-custom .item:after{
    background: #000000!important;
}

.chat-headPortrait{
    width: 100%;
    height: 100%;
    border-radius: 50%;
}

.chat-head{
    width: 100rpx;
    height: 100rpx;
    margin-left: 30rpx;
    flex: none;
}

.chat-end{
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24rpx;
    color: #8B8B8B;
    margin-bottom: 40rpx;
}

.chat-main{
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
}

.chat-row{
    margin: 30rpx 0;
}

.chat-name{
    font-size: 26rpx;
    color: #7C7C7C;
    padding-left: 20rpx;
    margin-bottom: 10rpx;
}

.chat-time{
    font-size: 24rpx;
    color: #b7b7b7;
    padding-left: 12rpx;
    margin-top: 16rpx;
}

.chat-msg{
    display: inline-block;
    position: relative;
    max-width: calc(100% - 100rpx);
    background-color: #0093A2;
    padding: 26rpx 30rpx;
    border-radius: 10rpx;
    font-size: 28rpx;
    word-wrap: break-word;
    color: #FEFEFE;
    margin-left: 6rpx;
    /*font-weight: bold;*/
    min-height: 90rpx;
}

.chat-msg:after{
    content: '';
    position: absolute;
    width: 0;
    height: 0;
    border-width: 14rpx 20rpx;
    border-style: solid;
    border-color: transparent transparent transparent #0093A2;
    right: -40rpx;
    top: 30rpx;
}

.chat-custom{

}

.chat-custom .row{
    display: flex;
    width: 100%;
    margin: 10rpx 0;
}

.chat-custom .row-left{
    width: 150rpx;
    flex: none;
}

.chat-custom .row-right{
    flex: auto;
    white-space: initial;
}

.chat-custom .row .item{
    position: relative;
    margin-right: 32rpx;
}

.chat-custom .item:after{
    content: '';
    width: 2rpx;
    height: 30rpx;
    background: #ffffff;
    position: absolute;
    right: -16rpx;
    top: 50%;
    transform: translateY(-50%);
}

.chat-custom .row .item:last-child:after{
    width: 0;
}

.btn-wrap{
    display: flex;
    flex-direction: column;
    align-items: center;
}

.btn-wrap .item{
    width: 80%;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100rpx;
    margin: 20rpx 0;
    border-radius: 20rpx;
    background: dodgerblue;
    color: white;
    font-size: 40rpx;
    font-weight: bold;
    z-index: 9999;
}

/* 图片消息 */
.chat-img{
    width: 100%;
    margin: 30rpx 0;
    display: flex;
    flex-grow: 1;
    justify-content: flex-end;
}

.chat-pic {
    max-width: 360rpx;
    height: 360rpx;
    background-repeat: no-repeat;
    background-size: center;
    background-position: center;
    border-radius: 4rpx;
    position: relative;
}

.chat-progress{
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: rgba(29, 29, 29, 0.548);
    color: #fff;
}

@keyframes loader {
    0% {
        -webkit-transform:rotate(0deg);
        transform:rotate(0deg);
    }
    100% {
        -webkit-transform:rotate(360deg);
        transform:rotate(360deg);
    }
}

.loader {
    border-top:8rpx solid rgba(0,0,0,0.1);
    border-right:8rpx solid rgba(0,0,0,0.1);
    border-bottom:8rpx solid rgba(0,0,0,0.1);
    border-left:8rpx solid #555;
    transform:translateZ(0);
    animation:loader 600ms infinite linear;
    transition:all 500ms ease;
    opacity:1;
}

.loader,.loader:after {
    border-radius:50%;
    width:40rpx;
    height:40rpx;
}

/* 底部样式 */
.chat-footer{
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    background:rgba(248,248,248,1);
    box-sizing: border-box;
}

.chat-input-box{
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #ffffff;
    padding: 20rpx 25rpx;
}

.chat-wait{
    display: flex;
    align-items: center;
    justify-content: center;
    height: 120rpx;
    background: #ffffff;
    font-size: 26rpx;
}

.msg-icon{
    width: 58rpx;
    height: 58rpx;
}

.chat-input-box .input{
    height:76rpx;
    line-height: 76rpx;
    background:rgba(255,255,255,1);
    border: none;
    border-radius:6rpx;
    font-size: 30rpx;
    padding:0 30rpx;
    display: flex;
    flex-direction: row;
    align-items: center;
    position: relative;
    flex-grow: 1;
    background: #F1F1F1;
}

.inputArea{
    width: 100%;
    height: 98%;
    flex-grow: 1;
}
.placeHolder{
    position: absolute;
    font-size: 26rpx;
    color: #cccccc;
    height: 100%;
    box-sizing: border-box;
    top: 0;
    z-index: 0;
}
.footer-send{
    height: 76rpx;
    border-radius: 12rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 26rpx;
    margin-left: 20rpx;
    padding: 0 10rpx;
}
.footer-send-item{
    font-size: 60rpx;
    font-weight: bold;
}
.footer-h{
    position: fixed;
    top: 100px;
}
.more{
    display: flex;
    justify-content: center;
    align-items: center;
}
.more-text{
    padding: 6rpx 14rpx;
    background:rgba(216,216,216,1);
    border-radius:4rpx;
    color: #FFFFFF;
    font-size: 20rpx;
    margin: 30rpx auto;
}

.open-more{
    display: flex;
    flex-wrap: wrap;
    padding: 40rpx 0;
    min-height: 360rpx;
}

.open-more-item{
    flex-basis: 25%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-items: center;
}

.open-more-item .msg-img{
    width: 100rpx;
    height: 100rpx;
    margin-bottom: 16rpx;
}

.open-more-item .msg-text{
    font-size: 28rpx;
    color: #888888;
}


.loader-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 80rpx;
    width: 100%;
    opacity:1;
    z-index:99;
    color: #b7b7b7;
    font-size: 28rpx;
}

.loader-tip{
    background: #E7E7E7;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #8B8B8B;
    padding:20rpx 30rpx;
    font-size: 24rpx;
    border-radius: 6rpx;
}

.loader {
    border-top:8rpx solid rgba(0,0,0,0.1);
    border-right:8rpx solid rgba(0,0,0,0.1);
    border-bottom:8rpx solid rgba(0,0,0,0.1);
    border-left:8rpx solid #555;
    transform:translateZ(0);
    animation:loader 600ms infinite linear;
    transition:all 500ms ease;
    opacity:1;
}

.loader,.loader:after {
    border-radius:50%;
    width:40rpx;
    height:40rpx;
}

@keyframes loader {
      0% {
          -webkit-transform:rotate(0deg);
          transform:rotate(0deg);
      }
      100% {
          -webkit-transform:rotate(360deg);
          transform:rotate(360deg);
      }
}

.z-btn-wrap{
    display: flex;
    align-items: center;
    justify-content: center;
    height: 120rpx;
    background: #ffffff;
}

.z-btn{
    background: #06CAD8;
    color: #ffffff;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 24rpx 40rpx;
    font-size: 32rpx;
    font-weight: 500;
    border-radius: 10rpx;
    min-width: 400rpx;
}


(3)index.js

import TIM from "tim-wx-sdk";

const {getDatePattern,isJSON} = getApp().require('utils/util');

let DATA_APP = getApp() && getApp().globalData || {};
let DATA_TIM = DATA_APP.TIM || {};
let TIM_MSG = [];

Page({
    data: {
        colToView: '',
        colUserId: '自己的IM userID',
        colUserOtherId: '对方的IM userID',
        colSendMsg: '',
        colPercent: 0,

        pageLastLength: 1,
        pageNext: '',
        pageCompleted: '',

        showSendMore: false,
        showLoading: false,
        showThree: false,
        showEnd: false,

        arrMsg: [],
        arrImg: [],
    },

    onLoad(options) {

        this.TIM_getMsgList();

        //订阅初始化获取聊天记录
        wx.event.on('conversationInit', () => {
            this.TIM_getMsgList();
        });

        //订阅收到消息
        wx.event.on('received', (e) => {
            this.TIM_setGlobalMsg(e,'received');
        });

    },

    //im获取消息、打开某个会话时,第一次拉取消息列表
    TIM_getMsgList() {
        this.onShowLoading();
        let param = {
            conversationID: 'C2C' + this.data.colUserOtherId,
            count: 15,
            nextReqMessageID: this.data.pageNext
        };
        let promise = DATA_TIM.getMessageList(param);
        promise.then((imResponse) => {
            this.TIM_setGlobalMsg(imResponse, 'load', true);
        });
    },

    //im获取消息、打开某个会话时,第一次拉取消息列表
    TIM_getMsgListMore() {
        this.onShowLoading();
        let param = {
            conversationID: 'C2C' + this.data.colUserOtherId,
            count: 15,
            nextReqMessageID: this.data.pageNext
        };
        let promise = DATA_TIM.getMessageList(param);
        promise.then((imResponse) => {
            this.TIM_setGlobalMsg(imResponse, 'load');
        });
    },

    //im创建文本消息
    TIM_createMsg() {
        let param = {
            to: this.data.colUserOtherId,
            conversationType: TIM.TYPES.CONV_C2C,
            payload: {
                text: this.data.colSendMsg
            }
        };
        let message = DATA_TIM.createTextMessage(param);
        this.TIM_sendMessageFun(message);
        this.onClearInput();
    },

    //im创建自定义消息
    TIM_createMsgCustom(msg) {
        let paramText = {
            name:'名字',
            sex:'男',
            age:'27',
            weight:'60kg',
            height:'171cm',
            desc:'长期肥胖,需要减肥',
            descTime:'一年',
            history:'暂无',
            allergy:'无',
            type:'describe'
        };
        let param = {
            to: this.data.colUserOtherId,
            conversationType: TIM.TYPES.CONV_C2C,
            payload: {
                data: JSON.stringify(paramText), // 用于标识该消息是骰子类型消息
                description: '描述主题', // 获取骰子点数
                extension: '描述说明'
            }
        };
        let message = DATA_TIM.createCustomMessage(param);
        this.TIM_sendMessageFun(message);
    },

    // im创建图片- 选择图片
    TIM_createPhoto(e) {
        let that = this;
        let name = e.currentTarget.dataset.name;
        if (name === 'album') {
            that.TIM_createPhotoNow(name)
        } else if (name === 'camera') {
            wx.getSetting({
                success: function (res) {
                    if (!res.authSetting['scope.camera']) { // 无权限,跳转设置权限页面
                        wx.authorize({
                            scope: 'scope.camera',
                            success: function () {
                                that.TIM_createPhotoNow(name)
                            }
                        })
                    } else {
                        that.TIM_createPhotoNow(name)
                    }
                }
            })
        }
    },

    // im创建图片- 创建完成
    TIM_createPhotoNow(name) {
        let that = this;
        let colUserId = this.data.colUserId;
        wx.chooseImage({
            sourceType: [name],
            count: 1,
            success: (res) => {
                // 在发送之前先push进去一张图片
                let messageList = that.data.arrMsg;
                let data = {
                    type: 'TIMImageElem',
                    send: true,
                    from: colUserId,
                    showSendMore: res.tempFilePaths[0]
                };
                messageList.push(data);
                that.setData({
                    arrMsg: messageList
                });
                that.onHideSendMore();
                that.pageScrollToBottom(true);
                // 2. 创建消息实例,接口返回的实例可以上屏
                let message = DATA_TIM.createImageMessage({
                    to: that.data.colUserOtherId, // 消息的接收方,
                    conversationType: TIM.TYPES.CONV_C2C,
                    payload: {file: res},
                    onProgress: (event) => {
                        event = event || 0;
                        that.setData({
                            colPercent: event * 100
                        })
                    }
                });
                that.TIM_sendMessageFun(message,'TIMImageElem')
            }
        })
    },

    //im发送-处理
    TIM_sendMessageFun(message, type) {
        DATA_TIM.sendMessage(message).then((imResponse) => {
            // 发送成功
            if (type === 'TIMImageElem') {
                let messageList = this.data.arrMsg;
                messageList.pop();
                this.setData({
                    arrMsg: messageList
                })
            }
            this.TIM_setGlobalMsg(imResponse, 'send');
            this.onHideSendMore();
            this.onClearInput();
        }).catch((imError) => {
            console.warn('发送失败:', imError);
        })
    },

    //im处理数据
    TIM_setGlobalMsg(imResponse, type, loadFirst) {
        console.log('消息列表',imResponse);
        if (type === 'send'||type === 'received') {
            let data = {};
            if(type === 'received'){
                data = imResponse.data[0];
                // this.setData({
                //     showThree:false,
                // })
            }else {
                data = imResponse.data.message||{};
            }
            let arrMsg = this.data.arrMsg;
            let arrImg = this.data.arrImg;
            if (data.type === 'TIMImageElem') {
                arrImg.push(data.payload.imageInfoArray[0].url);
            }
            if(data.type === 'TIMCustomElem') {
                data.dataCustom = isJSON(data.payload.data)? JSON.parse(data.payload.data):{};
            }
            data.timeFormat = getDatePattern(new Date(data.time * 1000), 'yyyy-MM-dd HH:mm');
            arrMsg.push(data);
            this.setData({
                arrMsg: arrMsg,
                arrImg: arrImg,
            }, () => {
                this.pageScrollToBottom(true)
            });
        }else {
            let data = imResponse.data||{};
            let arrData = data.messageList || [];
            let arrImg = [];
            arrData = arrData.map(x => {
                if (x.type === 'TIMImageElem') {
                    arrImg.push(x.payload.imageInfoArray[0].url);
                }
                if(x.type === 'TIMCustomElem') {
                    x.dataCustom = isJSON(x.payload.data)? JSON.parse(x.payload.data):{};
                }
                x.timeFormat = getDatePattern(new Date(x.time * 1000), 'yyyy-MM-dd HH:mm')
                return x
            });
            arrImg = arrImg.concat(this.data.arrImg);
            TIM_MSG = arrData.concat(this.data.arrMsg);// 全局消息列表
            this.setData({
                'arrMsg': TIM_MSG,
                'arrImg': arrImg,
                'pageNext': data.nextReqMessageID,// 用于续拉,分页续拉时需传入该字段。
                'pageCompleted': data.isCompleted,// 表示是否已经拉完所有消息。
                'pageLastLength':arrData.length//用户定位滚动位置
            }, () => {
                this.onHideLoading();
                this.pageScrollToBottom(loadFirst)
            });
        }
    },

    //实时更新输入框的数据
    onInputMsg(e) {
        this.setData({
            'colSendMsg': e.detail.value
        })
    },

    onClearInput() {
        this.setData({
            'colSendMsg': ''
        })
    },

    //显示加载框
    onShowLoading() {
        this.setData({
            'showLoading': true
        });
    },

    //隐藏加载框
    onHideLoading() {
        this.setData({
            'showLoading': false
        });
    },

    // 点更多出现图片和相册
    onShowSendMore() {
        this.setData({
            showSendMore: true
        })
    },

    // 点击屏幕 发消息更多的弹框下去
    onHideSendMore() {
        this.setData({
            showSendMore: false
        })
    },

    // 预览
    previewImage(e) {
        let url = e.currentTarget.dataset.url;
        wx.previewImage({
            current: url, // 当前显示图片的http链接
            urls: this.data.arrImg
        })
    },


    //滚动到页面底部
    pageScrollToBottom(isBottom) {
        let index = null;
        if (isBottom) {
            index = 'msg-' + (this.data.arrMsg.length - 1);
        } else {
            index = 'msg-' + 0;
        }
        this.setData({
            colToView: index
        })
    },

    //加载更多
    onLoadMore() {
        if (!this.data.pageCompleted) {
            this.TIM_getMsgListMore()
        }
    },

});

四:资料

五:我的项目部分截图

在这里插入图片描述 在这里插入图片描述