介绍

原生聊天会话详情组件,支持Android和iOS双端,仅限App端nvue页面可用,不支持H5、小程序等。

样式

注:Android和iOS大同小异,想看更详细的演示,见下方演示视频。
Android ····················· iOS

痛点

uni-app上,常规使用 vue 或使用 nvue 去实现聊天页面,发送消息等视图,体验性差,性能低下。包括不限于键盘与表情面板等切换流畅度差,列表滑动卡顿,页面加载消息慢。

特性

解决了在 uni-app 上实现聊天详情页面的痛点,高性能聊天记录列表,无感知聊天记录置底,动画平滑切换键盘及面板,仿 Wechat 动画,支持发送消息、在离线状态展示。文字、语音、视频、图片、位置、文件等消息展示,支持下拉加载历史记录,表情,更多功能等...

重要

组件覆盖整个 nvue 页面,一组件即一页面,此组件仅为UI,通过传入参数(消息列表,会话信息,相关功能参数等)进行展示视图,不涉及具体聊天逻辑,具体聊天逻辑仍需uni-app JS端实现,使用请仔细阅读以下文档及注意事项。

演示视频

注:演示组件部分功能,如无大调整不会频繁更新演示视频,最新演示视频更新时间:2022-10-30

Android 平台

iOS 平台

演示 APP

目前仅提供 Android App 演示,进入 App 点击某会话选择->原生会话页面查看

付费定制

注:只接受正规合法项目定制开发,如有违法或擦边的,请勿联系。

由于编译型语言特性,原生插件无法过于可自定义化,所以客户虽然满意此插件的用户体验,但可能因自身项目原因对 UI 方面需要一定的改变。所以基于各种原因,此插件可支持付费定制需求,包括不限于 UI 的改变、消息结构的支持,多消息自定义类型的 UI 定制等。

费用包含:离线打包基础费用 + 需求定制费用

原生插件离线打包基础费用为:3000 元。每个客户仅需支付一次,如有客户自身的其他项目也需要定制另一种 UI,则不再收取此费用。

需求定制费用包含不限于会话页面的 UI 改变费用(根据具体需求定价)、多消息类型费用(单个类型的消息 UI,价格 500 起。此插件本身支持的消息类型不收费:文本图片视频语音等)、消息结构费用(一般根据你的 IMSDK 和消息类型数量定,价格 200-1000 间)。

举例说明:如果客户对 uniapp vue 做的聊天页面不满意,体验太差。此时需要一个原始聊天会话页面,刚好此原生插件的用户体验能够让你满意,但是消息支持度和页面的 UI 不太符合你当前项目的需求,需要进行一定的改变,那么你可以进行付费定制。如果主要需求是多消息类型支持,比如需要一个红包消息和一个订单消息,那么根据具体的 UI 定,这两个消息分别 500 起的价格。如果对页面整体 UI 没有太大的改变,那么总费用大约为:3000+500+500 = 4000 元。

付费方式

不要发票:淘宝、闲鱼、微信、支付宝

要发票:企业对公转账

微信扫一扫联系我

消息数据结构

消息数据结构支持度:

腾讯云即时通信 IM 消息结构支持度

支持以下接口消息数据:

点击查看腾讯云 IM 文字消息结构 JSON

YeIM 消息结构支持度

支持以下接口消息数据:

点击查看 YeIM-Uni-SDK 文档

融云即时通信 IM(uni-app SDK)消息结构支持度

支持以下接口消息数据:

  • 文本消息
  • 语音消息
  • 小视频消息
  • 图片消息
  • 撤回、删除消息

注:融云消息结构内必须添加 userInfo 字段,无 userInfo 字段消息无法显示

userInfo 字段对象结构:
{
"portrait":"https://img1.baidu.com/it/u=440202259,2734237708&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
"userId":"1",
"alias":"",
"name":"融云用户 1",
"extra":""
}

点击查看 融云即时通信 IM(uni-app SDK) 文档

目前支持腾讯云IM (uni-app SDK)YeIM-Uni-SDK融云即时通信IM(uni-app SDK)的消息结构数据,如需紧急支持其他类型,可参照上述已适配的消息结构 json 字符串去修改为类似的消息结构,再传入参数 messages

反馈与共建

  • 普通交流 QQ 群:391276294

  • 已付费 VIP 交流 QQ 群:499251579 (需提供插件市场购买的订单编号)

属性

属性名类型是否必填说明平台差异
optionsObject组件初始化参数,见下表详情
messagesArray<Object>消息数组,见下表详情(仅首次进入页面传入数组)

options

初始化参数

说明:组件初始化时有必传的一部分参数,均为组件渲染所需,请务必注意。

参数名类型默认值是否必填说明平台差异
structureStringtim消息结构平台,请根据实际情况准确填写,可选值:tim、yeim、rongcloud
  • tim:腾讯云 IM uni-app sdk
  • yeim:YeIM-Uni-SDK
  • rongcloud:融云 IM uni-app SDK(融云消息结构内必须添加 userInfo 字段,无 userInfo 字段消息无法显示)
  • userInfoObject当前用户信息,见下表详情
    conversationObject会话信息,见下表详情
    badgeNumber0导航栏未读角标
    badgeTextColorString#ffffff导航栏角标文字颜色1.0.1
    badgeBackgroundColorString#faab38导航栏角标背景色1.0.1
    showOnlineStatusBooleanfalse是否显示在、离线视图
    onlineStatusBooleanfalse在、离线状态(showOnlineStatus 为 true 时有效)
    showNameBooleanfalse是否显示对端用户名称
    amapWebKeyStringnull高德地图 Web Key,作为位置消息静态地图使用,如无位置消息,可不传
    toolsArray<Object>更多功能,见下表详情
    faceListArray<String>自定义收藏表情 URL 数组,见下表详情1.0.6
    popMenuListArray<Object>自定义消息长按气泡功能,见下表详情1.0.4
    rightTextBackgroundColorString#ffc166列表右侧消息框背景色1.0.1
    rightTextColorString#000000列表右侧消息框文字颜色1.0.1
    leftTextBackgroundColorString#ffffff列表左侧消息框背景色1.0.1
    leftTextColorString#ffffff列表左侧消息框文字颜色1.0.1
    sendButtonBackgroundColorString#ffffff发送按钮背景色1.0.1,iOS 端表现为表情面板按钮
    sendButtonTextColorString#ffffff发送按钮文字颜色1.0.1,iOS 端表现为表情面板按钮

    userInfo

    当前用户信息

    说明:用于区别聊天列表消息所属,消息发送者为 userInfoid ,则列表那一行的消息显示为右侧,否则为左侧

    参数名类型默认值是否必填说明平台差异
    idString当前用户 ID,字符串类型
    nameString当前用户名称,字符串类型。属于占位,此字段暂未使用

    conversation

    会话信息

    参数名类型默认值是否必填说明平台差异
    idString会话 ID,属于占位,此字段暂未使用
    nameString会话名称,字符串类型。组件导航栏视图使用此字段作为标题

    tools

    更多视图功能列表数据,下表为单个对象结构

    参数名类型默认值是否必填说明示例平台差异
    nameString功能名称相册
    iconString功能图标,支持static目录下的图片/static/album.png
    actionString点击事件名称,通过@tools-action事件回调到 JS 端,见下表详情album

    faceList

    自定义收藏表情 URL 数组

    faceList: ['url1', 'url2'];
    样式

    popMenuList

    消息长按气泡菜单,此处为新增功能。下表为单个对象结构

    参数名类型默认值是否必填说明示例平台差异
    nameString功能名称转发
    iconString功能图标,支持static目录下的图片/static/album.png
    actionString点击事件名称,通过@action事件回调到 JS 端forward
    样式

    messages

    注1:组件的`messages`属性仅在首次赋值时初始化列表(或者说列表没有消息时赋值),且仅初始化(赋值)一次,后续messages有任何变化均不会影响组件内现有列表。
    注2:实际应用中可直接传入SDK各个接口给出的Message对象数组,这里的必传参数指的是组件所需要的Message对象中的字段。如果传入的Message对象中没有以下必传参数,将忽略该条消息。
    注3:目前仅支持传入【腾讯云即时通信IM(Web & 小程序 & uni-app SDK】的消息结构,如果您使用的不是它,也可以使用,但需要修改相应的字段名称和代码逻辑。简单的来说,只要是给组件传入一个符合条件的json对象数组,组件内可以直接将消息显示出来。此处的腾讯IM只是更加方便,直接传入SDK给出的Message就能显示,不需要额外修改。更多消息结构后续将持续更新。

    messages接收一个消息对象数组

    腾讯云即时通信 IM

    腾讯云即时通信 IM 消息对象 点下方链接查看

    腾讯云即时通信 IM(Web & 小程序 & uni-app SDK)Message

    腾讯云 IM 必传参数

    参数名类型默认值是否必填说明平台差异
    IDString消息 ID
    typeString消息类型
    conversationTypeString消息所属会话的类型
    nickString消息发送者的昵称
    avatarString消息发送者的头像地址
    fromString发送方的 userID
    toString接收方的 userID
    isPeerReadBooleanC2C 消息对端是否已读
    timeNumber消息时间戳。单位:秒
    statusString消息状态
    payloadObject消息的内容,点此查看

    以下代码解释:给组件的 messages 属性赋值一个 messages 数组,messages 数组中包含了一个腾讯云 IM 文字消息对象

    <template>
    <view>
    <!-- 此处仅为演示初始化时消息数组结构传入,未给options等赋值,请勿直接复制使用 -->
    <yeim-chat-page :messages="messages" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {
    messages: [
    {
    ID: '144115242122962694-1666581007-32219913',
    conversationID: 'C2C2',
    conversationType: 'C2C',
    time: 1666581008,
    sequence: 1109260003,
    clientSequence: 1109260003,
    random: 32219913,
    priority: 'Normal',
    nick: '妖精不会飞',
    avatar:
    'https://img1.baidu.com/it/u=440202259,2734237708&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
    isPeerRead: true,
    nameCard: '',
    _elements: [
    {
    type: 'TIMTextElem',
    content: {
    text: '你好',
    },
    },
    ],
    isPlaceMessage: 0,
    isRevoked: false,
    from: '1',
    to: '2',
    flow: 'out',
    isSystemMessage: false,
    protocol: 'JSON',
    isResend: false,
    isRead: true,
    status: 'success',
    _onlineOnlyFlag: false,
    _groupAtInfoList: [],
    _relayFlag: false,
    atUserList: [],
    cloudCustomData: '',
    isDeleted: false,
    isModified: false,
    _isExcludedFromUnreadCount: false,
    _isExcludedFromLastMessage: false,
    clientTime: 1666581007,
    senderTinyID: '144115242122962694',
    readReceiptInfo: {},
    needReadReceipt: false,
    version: 0,
    isBroadcastMessage: false,
    payload: {
    text: '你好',
    },
    type: 'TIMTextElem',
    },
    ],
    };
    },
    };
    </script>

    Tips 消息类型

    插件版本 1.0.8 开始,支持腾讯云即时通信IM(Web & 小程序 & uni-app SDK)YeIM-Uni-SDK这两个 SDK 使用createCustomMessage创建自定义 Tips 消息,类似对方撤回了一条消息群主邀请了xxx进入了群聊这种 Tips 提示性的消息类型,需要根据指定的结构传入,方可显示如下 UI:

    腾讯云的自定义消息 Tips 消息类型消息结构显示:

    [
    {
    "ID": "144115242311226482-1686049232-82482770",
    "conversationID": "C2Cuser2",
    "conversationType": "C2C",
    "time": 1686049232,
    "sequence": 1900320001,
    "clientSequence": 1900320001,
    "random": 82482770,
    "priority": "Normal",
    "nick": "妖精不会飞",
    "avatar": "https://img1.baidu.com/it/u=440202259,2734237708&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
    "isPeerRead": false,
    "nameCard": "",
    "_elements": [
    {
    "type": "TIMCustomElem",
    "content": {
    "data": "tips",
    "description": "群主邀请了xxx、lll、zzz进入了群聊",
    "extension": ""
    }
    }
    ],
    "isPlaceMessage": 0,
    "isRevoked": false,
    "from": "1",
    "to": "user2",
    "flow": "out",
    "isSystemMessage": false,
    "protocol": "JSON",
    "isResend": false,
    "isRead": true,
    "status": "success",
    "_onlineOnlyFlag": false,
    "_groupAtInfoList": [],
    "_relayFlag": false,
    "atUserList": [],
    "cloudCustomData": "",
    "isDeleted": false,
    "isModified": false,
    "_isExcludedFromUnreadCount": false,
    "_isExcludedFromLastMessage": false,
    "clientTime": 1686049232,
    "senderTinyID": "144115242311226482",
    "readReceiptInfo": {},
    "needReadReceipt": false,
    "version": 0,
    "isBroadcastMessage": false,
    "payload": {
    "data": "tips",
    "description": "啊实打实大大是的",
    "extension": ""
    },
    "type": "TIMCustomElem"
    }
    ]

    YeIM-Uni-SDK 的自定义消息 Tips 消息类型消息结构显示:

    [
    {
    "sequence": 5740984,
    "messageId": "405076915945541-1681032133432-1",
    "userId": "user1",
    "conversationId": "user2",
    "conversationType": "private",
    "direction": "out",
    "from": "user1",
    "fromUserInfo": {
    "nickname": "没有毛病",
    "avatarUrl": "https://api.multiavatar.com/99521.png"
    },
    "to": "user2",
    "type": "custom",
    "body": {
    "custom": {
    "type": "tips",
    "data": "群主邀请了xxx、lll、zzz进入了群聊"
    }
    },
    "extra": "",
    "isRead": 0,
    "isRevoke": 0,
    "isDeleted": 0,
    "status": "success",
    "receive": 0,
    "time": 1681032133432
    }
    ]

    事件

    事件名参数说明平台差异
    @navigate-back导航栏左侧返回图标点击事件
    @navigate-more导航栏右侧更多图标点击事件
    @load-more聊天列表下拉加载事件,配合finishLoadMore()使用
    @avatar-actionObject聊天列表头像点击事件
    @sendObject发送按钮点击事件
    @voice-endObject语音完毕回调
    @tools-actionObject功能点击事件回调,回调参数为初始化参数tools内传入的action
    @open-fileObject文件消息点击事件
    @open-mapObject位置消息点击事件
    @revoke-messageObject消息菜单->撤回消息点击事件
    @delete-messageObject消息菜单->删除消息点击事件
    @add-custom-emoji-actionObject自定义收藏表情面板点击添加事件1.0.4
    @custom-emoji-actionObject自定义收藏表情点击事件1.0.6
    @custom-emoji-long-actionObject自定义收藏表情长按事件1.0.6
    @error-message-actionObject错误消息红色感叹号图标点击事件1.0.8

    有参事件代码演示

    <template>
    <view>
    <!-- 此处仅为演示事件,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page @avatar-action="@avatarAction" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    methods: {
    avatarAction(e) {
    console.log(e);
    /*
    当用户点击消息列表的头像后,此处回调结果:
    {
    "type":"avatarAction",
    "timeStamp":1666691669115,
    "target":{
    "id":"",
    "dataset":{
    },
    "offsetLeft":0,
    "offsetTop":0
    },
    "currentTarget":{
    "id":"",
    "dataset":{
    },
    "offsetLeft":0,
    "offsetTop":0
    },
    "detail":{
    "messageId":"144115242122962694-1666684242-36090699"
    },
    "stopPropagation":"function() { [native code] }"
    }
    */
    },
    },
    };
    </script>

    方法

    注意:使用方法需先获取组件实例,即通过refs获取

    setBadge(int)

    设置导航栏未读角标,直接传入数字,0则不显示角标

    <template>
    <view>
    <!-- 此处仅为演示方法,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page ref="chatpage" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    onReady() {
    this.$refs.chatpage.setBadge(5);
    },
    };
    </script>

    setOnlineStatus(boolean)

    设置在、离线状态,直接传入布尔值。调用此方法需确认组件参数showOnlineStatustrue,否则无效

    <template>
    <view>
    <!-- 此处仅为演示方法,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page ref="chatpage" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    onReady() {
    this.$refs.chatpage.setOnlineStatus(true);
    },
    };
    </script>

    finishLoadMore(Array<Object>)

    完成加载历史记录,传入消息数组。没有记录则传入[],不管有无数据,回调@load-more事件后,均需调用此方法

    <template>
    <view>
    <!-- 此处仅为演示方法,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page ref="chatpage" @load-more="loadMore" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    method: {
    loadMore() {
    setTimeout(() => {
    // 模拟加载历史记录
    let messages = getHistoryList();
    // 完成加载历史记录
    this.$refs.chatpage.finishLoadMore(messages);
    // TODO
    // 如果没有更多历史记录
    // this.$refs.chatpage.finishLoadMore([]);
    }, 2000);
    },
    },
    };
    </script>

    insertMessage(Object)

    将消息加入到聊天列表,参数为消息对象

    注:此处使用腾讯IM部分接口演示发送消息及收到消息,部分代码不完善,请勿直接复制使用。
    <template>
    <view>
    <!-- 此处仅为演示方法,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page ref="chatpage" @send="send" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    onLoad() {
    //当收到新消息时候,调用链:onMessage->insertMessage
    TIM.on(TIM.EVENT.MESSAGE_RECEIVED, this.onMessage);
    },
    method: {
    onMessage(e) {
    let list = e.data;
    for (let i = 0; i < list.length; i++) {
    //TODO 此处应做去重处理
    let message = list[i];
    this.$refs.chatpage.insertMessage(message);
    }
    },
    send(e) {
    let detail = e.detail;
    let text = detail.text;
    let message = TIM.createTextMessage({
    to: '对方ID',
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    text: text,
    },
    });
    this.$refs.chatpage.insertMessage(message);
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    //发送消息一般需要耗时,但若等待发送完成后再将消息插入到消息列表,那么用户是有感知的,明显会感觉到按下发送按钮后,等了一下消息才弹出来。
    //基于这种情况,我们可以在创建消息后直接调用insertMessage,此时消息结构中,消息状态一般为`发送中`,那么我们在调用发送接口发送完成后,再调用updateMessageStatus,就可以刷新当前消息状态
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    })
    .catch((imError) => {
    message.status = 'fail';
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    });
    },
    },
    };
    </script>

    removeMessage(Object)

    从消息列表中删除某一条消息,参数为消息对象

    updateMessage(Object)

    在消息列表里刷新某一条消息,参数为消息对象

    updateMessageStatus(Object)

    在消息列表中刷新某一条消息的状态,参数为消息对象

    注:发送消息一般需要耗时,但若等待发送完成后再将消息插入到消息列表,那么用户是有感知的,明显会感觉到按下发送按钮后,等了一下消息才弹出来。基于这种情况,我们可以在创建消息后直接调用insertMessage,此时消息结构中,消息状态一般为`发送中`,那么我们在调用发送接口发送完成后,再调用updateMessageStatus,就可以刷新当前消息状态

    addCustomFace(String)

    在自定义收藏表情面板中添加一个表情,参数为图片 URL

    deleteCustomFace(String)

    在自定义收藏表情面板中删除一个表情,参数为图片 URL

    restore()

    恢复部分设置,退出聊天页面时调用,可恢复因组件需要而改变的全局设置。

    注:目前需要调用此方法的目的是恢复设置:全局全屏设置。异常情况为Android11以上,组件会全屏化,退出组件所在的页面时,如果不调用此方法会导致导航栏及底部输入框置底(忽略安全条,UI较为难看)
    <template>
    <view>
    <!-- 此处仅为演示方法,未初始化参数,请勿直接复制使用 -->
    <yeim-chat-page ref="chatpage" />
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    onBackPress() {
    this.$refs.chatpage.restore();
    },
    };
    </script>

    使用样例

    此样例使用腾讯云即时通信 IM SDK演示,SDK 名称:tim-wx-sdk、tim-upload-plugin,文档地址:https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html#getMessageList

    YeIM-Uni-SDK 样例可查看代码:https://ext.dcloud.net.cn/plugin?id=10266

    融云 IM uni-app sdk 使用样例可查看代码:https://gitee.com/wzJun1/yeim-chat-page_rongyuncloud_demo

    会话列表页

    <template>
    <view>
    <button class="button" @click="open">打开某个用户的聊天会话页面</button>
    </view>
    </template>
    <script>
    export default {
    data() {
    return {};
    },
    methods: {
    open() {
    //由于腾讯IM获取历史聊天记录无缓存情况下会请求网络,导致进入会话详情页面,组件无法第一时间给messages赋值,从而影响用户体验
    //所以此处建议在进入页面之前获取聊天记录,使用全局变量或者Vuex的方式给会话页面传值。
    //Tips:如果可以保证在会话页面里拉取聊天记录的速度,或者可以保证直接从本地缓存里拉取,也可以不在此处获取,直接在会话详情页面onLoad里getMessageList
    let promise = TIM.getMessageList({
    conversationID: '此处填会话ID',
    count: 15,
    });
    promise
    .then((imResponse) => {
    console.log(imResponse);
    //TODO
    //此处按上述原因,全局变量赋值imResponse
    //可使用globalData或者Vuex实现
    //我在此处未写具体实现逻辑,这里应自行实现
    // eg: getApp().globalData.imResponse = imResponse;
    // preloadPage 预加载nvue页面,保证了渲染效率
    // `/pages/index/chat` 为demo中组件所在页面,也即是下方代码块所显示内容
    // 会话ID和会话名称也需自行替换
    // 此处的url可自行修改,如果有其他方式去给会话详情页面传递会话信息,也可以直接去掉id、name
    let url =
    '/pages/index/chat?id=' +
    会话ID +
    '&name=' +
    会话名称 +
    '&time=' +
    new Date().getTime();
    uni.preloadPage({
    url: url,
    complete: () => {
    setTimeout(() => {
    uni.navigateTo({
    url: url,
    });
    }, 100);
    },
    fail: (e) => {
    console.log('页面预加载失败:' + e);
    },
    });
    })
    .catch(function (imError) {
    console.warn('getMessageList error:', imError);
    });
    },
    },
    };
    </script>

    page.json

    注1:因ye组件特性,此组件覆盖了一整个页面,所以组件内部的手势滑动操作均由组件决定。因此我们需要把页面的滚动和手势禁用。
    注2:页面的路径并不是一定要pages/index/chat,只要是包含了ye组件的页面就需做此配置,而这里仅做示例。
    {
    "path": "pages/index/chat",
    "style": {
    "enablePullDownRefresh": false,
    "disableScroll": true,
    "navigationStyle": "custom",
    "navigationBarTextStyle": "black"
    }
    }

    聊天会话详情页面

    <template>
    <view>
    <yeim-chat-page
    ref="chatpage"
    :style="style"
    :options="options"
    :messages="messages"
    @navigate-back="navigateBack"
    @navigate-more="navigateMore"
    @load-more="loadMore"
    @avatar-action="avatarAction"
    @send="sendTextMessage"
    @voice-end="voiceEnd"
    @tools-action="toolsAction"
    @revoke-message="revokeMessage"
    @delete-message="deleteMessage"
    >
    </yeim-chat-page>
    </view>
    </template>
    <script>
    export default {
    data() {
    return {
    time: 0,
    style: {
    width: uni.getSystemInfoSync().screenWidth + 'px',
    height: uni.getSystemInfoSync().screenHeight + 'px',
    },
    options: {},
    conversation: {},
    messages: [],
    nextReqMessageID: '',
    isCompleted: false,
    };
    },
    onLoad(option) {
    //接收url参数,转会话信息
    this.conversation.id = option.id;
    this.conversation.name = option.name;
    //url random,用于解除预加载
    this.time = option.time;
    //组件初始化参数
    this.options = {
    structure: 'tim', //消息结构类型: tim
    userInfo: {
    id: 'userid1',
    name: '用户本人',
    }, // 当前登录用户
    conversation: this.conversation, // 会话用户信息(群信息):name
    badge: 10, // 未读角标
    showOnlineStatus: true, //是否显示在线状态
    onlineStatus: true, // 是否在线
    showName: false, //是否显示列表内除自己外,别人的名称
    amapWebKey: '高德web key', // 如需要位置消息,则需填写高德地图 Web Key用作显示位置消息的静态地图
    //更多功能
    tools: [
    {
    name: '相册',
    icon: '/static/ic_more_item_image.png',
    action: 'album',
    },
    {
    name: '视频',
    icon: '/static/ic_more_item_cap.png',
    action: 'photograph',
    },
    {
    name: '视频通话',
    icon: '/static/ic_more_item_video_call.png',
    action: 'videoCall',
    },
    {
    name: '语音通话',
    icon: '/static/ic_more_item_voice_call.png',
    action: 'voiceCall',
    },
    {
    name: '文件',
    icon: '/static/ic_more_item_files.png',
    action: 'files',
    },
    {
    name: '位置',
    icon: '/static/ic_more_item_location.png',
    action: 'location',
    },
    ],
    };
    //此处承接上个页面的:imResponse(全局变量或者Vuex赋值)
    //这里假设我已经拿到了imResponse
    // eg: const imResponse = getApp().globalData.imResponse;
    const messageList = imResponse.data.messageList;
    this.nextReqMessageID = imResponse.data.nextReqMessageID;
    this.isCompleted = imResponse.data.isCompleted;
    //给组件 messages 赋值
    this.messages = messageList;
    },
    onShow() {
    //监听腾讯IM MESSAGE_RECEIVED时间,收到消息后调用onMessage
    TIM.on(TIM.EVENT.MESSAGE_RECEIVED, this.onMessage, this);
    },
    onUnload() {
    //解除预加载
    let url =
    '/pages/index/chat?id=' +
    this.conversation.id +
    '&name=' +
    this.conversation.name +
    '&time=' +
    this.time;
    uni.unPreloadPage({
    url: url,
    });
    //取消监听MESSAGE_RECEIVED
    TIM.off(TIM.EVENT.MESSAGE_RECEIVED, this.onMessage);
    },
    onBackPress() {
    //执行恢复设置,防止UI错位
    this.$refs.chatpage.restore();
    //解除预加载
    let url =
    '/pages/index/chat?id=' +
    this.conversation.id +
    '&name=' +
    this.conversation.name +
    '&time=' +
    this.time;
    uni.unPreloadPage({
    url: url,
    });
    //取消监听MESSAGE_RECEIVED
    TIM.off(TIM.EVENT.MESSAGE_RECEIVED, this.onMessage);
    },
    methods: {
    onMessage: function (e) {
    //收到消息,插入到消息列表中
    try {
    let list = e.data;
    if (list) {
    for (let i = 0; i < list.length; i++) {
    let message = list[i];
    //去重、防乱入
    if (message.from === this.conversation.id) {
    let index = this.messages.findIndex((item) => item.ID === message.ID);
    if (index === -1) {
    //messages可加,也可不加,此处push进去,是可能后续需要用到messages
    //组件的messages属性,仅在第一次赋值时生效,以后修改messages不影响组件内部消息列表
    this.messages.push(message);
    //插入到消息列表
    this.$refs.chatpage.insertMessage(message);
    }
    }
    }
    }
    } catch (e) {
    console.log(e);
    }
    },
    navigateBack() {
    //左侧导航栏返回图标点击事件
    },
    navigateMore() {
    //右侧导航栏更多图标点击事件
    },
    avatarAction(e) {
    //头像点击事件
    console.log(e);
    },
    loadMore() {
    //下拉加载历史记录回调
    if (this.nextReqMessageID != '' && !this.isCompleted) {
    let promise = TIM.getMessageList({
    conversationID: 'C2C' + this.conversation.id,
    nextReqMessageID: this.nextReqMessageID,
    count: 15,
    });
    promise.then((imResponse) => {
    const messageList = imResponse.data.messageList; // 消息列表。
    this.nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
    this.isCompleted = imResponse.data.isCompleted;
    this.$refs.chatpage.finishLoadMore(messageList);
    });
    } else {
    this.$refs.chatpage.finishLoadMore([]);
    }
    },
    sendTextMessage(e) {
    //发送按钮点击事件
    let detail = e.detail;
    let text = detail.text;
    if (text != '') {
    let message = TIM.createTextMessage({
    to: this.conversation.id,
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    text: text,
    },
    });
    this.messages.push(message);
    this.$refs.chatpage.insertMessage(message);
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    })
    .catch((imError) => {
    console.warn('sendMessage error:', imError);
    message.status = 'fail';
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    });
    }
    },
    voiceEnd(e) {
    //组件内,用户语音完毕事件,只有用户确认发送语音后,才会回调此方法
    const message = TIM.createAudioMessage({
    to: this.conversation.id,
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    file: e.detail,
    },
    });
    this.messages.push(message);
    this.$refs.chatpage.insertMessage(message);
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    })
    .catch(function (imError) {
    console.warn('sendMessage error:', imError);
    message.status = 'fail';
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    });
    },
    revokeMessage(e) {
    //撤回消息回调
    let messageId = e.detail.messageId;
    let message = this.messages.find((item) => item.ID == messageId);
    uni.showLoading({
    title: '撤回中...',
    });
    let promise = TIM.revokeMessage(message);
    promise
    .then((imResponse) => {
    // 消息撤回成功
    uni.hideLoading();
    this.$refs.chatpage.updateMessage(imResponse.data.message);
    })
    .catch(function (imError) {
    uni.hideLoading();
    if (imError.code == 20016) {
    //消息撤回超过了时间限制(默认2分钟)
    uni.showModal({
    title: '提示',
    content: '消息撤回超过了时间限制2分钟',
    });
    }
    // 消息撤回失败
    console.warn('revokeMessage error:', imError);
    });
    },
    deleteMessage(e) {
    //删除消息回调
    let messageId = e.detail.messageId;
    let message = this.messages.find((item) => item.ID == messageId);
    uni.showLoading({
    title: '删除中...',
    });
    let promise = TIM.deleteMessage([message]);
    promise
    .then((imResponse) => {
    // 删除消息成功
    uni.hideLoading();
    this.$refs.chatpage.removeMessage(message);
    })
    .catch(function (imError) {
    uni.hideLoading();
    // 删除消息失败
    console.warn('deleteMessage error:', imError);
    uni.showToast({
    icon: 'error',
    title: '删除失败',
    });
    });
    },
    toolsAction(e) {
    //更多功能点击事件
    if (e.detail) {
    let detail = e.detail;
    let action = detail.action;
    if (action == 'album') {
    uni.chooseImage({
    count: 1,
    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
    success: (res) => {
    let message = TIM.createImageMessage({
    to: this.conversation.id,
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    file: res,
    },
    onProgress: function (event) {
    console.log('file uploading:', event);
    },
    });
    this.messages.push(message);
    this.$refs.chatpage.insertMessage(message);
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    })
    .catch(function (imError) {
    console.warn('sendMessage error:', imError);
    message.status = 'fail';
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    });
    },
    });
    } else if (action == 'photograph') {
    uni.chooseVideo({
    count: 1,
    maxDuration: 60, // 设置最长时间60s
    camera: 'back', // 后置摄像头
    success: (res) => {
    let message = TIM.createVideoMessage({
    to: this.conversation.id,
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    file: res,
    },
    onProgress: function (event) {
    console.log('file uploading:', event);
    },
    });
    this.messages.push(message);
    this.$refs.chatpage.insertMessage(message);
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    })
    .catch(function (imError) {
    console.warn('sendMessage error:', imError);
    message.status = 'fail';
    this.$refs.chatpage.updateMessageStatus(imResponse.data.message);
    });
    },
    });
    } else if (action == 'files') {
    } else if (action == 'location') {
    uni.chooseLocation({
    success: (res) => {
    let message = TIM.createLocationMessage({
    to: this.conversation.id,
    conversationType: TIM.TYPES.CONV_C2C,
    payload: {
    description: res.name + '|' + res.address,
    longitude: res.longitude, // 经度
    latitude: res.latitude, // 纬度
    },
    });
    let promise = TIM.sendMessage(message);
    promise
    .then((imResponse) => {
    this.messages.push(imResponse.data.message);
    this.$refs.chatpage.insertMessage(imResponse.data.message);
    })
    .catch(function (imError) {
    // 发送失败
    console.warn('sendMessage error:', imError);
    });
    },
    });
    }
    }
    },
    },
    };
    </script>

    Demo

    https://gitee.com/wzJun1/ye-im-chat-page-demo-tim

    更新日志

    1.1.0 - 2023-10-08

    • 新增支持融云 uni-app sdk 消息结构

    1.0.9 - 2023-07-28

    • 修复在 Android 上因缺少依赖导致的闪退

    1.0.8 - 2023-06-06

    1.0.7 - 2023-04-02

    • 1.修复 点击表情面板后未切换输入框
    • 2.修复 传递图片 URL 包含中文的情况下无法正常显示

    1.0.6 - 2023-03-22

    • 1.新增 自定义表情面板 样式见详情
    • 2.优化 emoji 表情面板
    • 3.修复 点击 emoji 表情时 placeholder 异常问题

    1.0.5 - 2023-03-14

    • 1.修复 Android 语音消息时长 UI 显示异常
    • 2.修复 Android iOS 语音发送时权限弹窗导致的异常问题
    • 3.修复 Android iOS 离开页面时语音未停止
    • 4.修复 Android 会话标题居中问题
    • 5.优化 图片消息自适应尺寸

    1.0.4 - 2023-02-19

    • 1.新增 可添加消息长按气泡的自定义选项,例如可自行新增长按消息菜单中的:转发收藏
    • 2.新增 键盘输入事件 @input
    • 3.新增 Android 图片长按保存
    • 4.优化 Android 发送后消息滚动到底部
    • 5.修复 Android 设置消息样式(消息背景色)时首次不生效
    • 6.修复 iOS 文件消息点击事件异常
    • 7.修复 iOS Android 当图片消息中图片尺寸相当且大于 500 时异常问题
    • 8.修复 iOS 消息长按气泡当返回页面时不消失的问题

    1.0.3 - 2023-02-15

    • 1.修复 iOS 端 UI 相关设置属性(options)异常问题
    • 2.修复 Android 端标签显示异常
    • 3.修复 Android 端消息长按菜单只有一个时,箭头显示异常
    • 3.修复 小米等手机录制语音消息异常

    1.0.2 - 2023-01-27

    • 1.增加 支持 YeIM-Uni-SDK 消息结构。YeIM-Uni-SDK是可以私有化部署的全开源即时通讯 UniAPP JSSDK,详情可查看文档。
    • 2.修复目前已知问题

    1.0.1 - 2022-11-14

    • 1.增加 部分自定义颜色:角标背景色、文字色,消息框背景色、文字色,按钮背景色、文字色
    • 2.修复 iOS 端切换表情、更多面板导致的列表异常上推
    • 3.修复 iOS 端更多视图显示异常

    1.0.0 - 2022-11-06

    • 1.首次发布原生会话页面组件类型插件