微信小程序即時(shí)通訊開發記錄(結合通訊雲IM) - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 小程序相關 >

微信小程序即時(shí)通訊開發記錄(結合通訊雲IM)

發表時(shí)間:2021-3-15

發布人(rén):融晨科技

浏覽次數:121

前言

最近接到(dào)一個(gè)需求是(shì)在(zài)原項目的(de)基礎上(shàng)去開發一個(gè)即時(shí)通訊的(de)功能,主要(yào / yāo)是(shì)一對一的(de)聊天,供客戶和(hé / huò)客戶之(zhī)間進行即時(shí)通訊,功能主要(yào / yāo)包括聊天列表、發送文字、發送表情、發送圖片、發送視頻、發送語音、發送自定義信息等功能。因爲(wéi / wèi)項目需求,所以(yǐ)選擇了(le/liǎo)騰訊雲IM來(lái)開發。

開發流程

首先是(shì)要(yào / yāo)注冊騰訊雲并創建應用,拿到(dào)APPID和(hé / huò)秘鑰。

文檔位置: cloud.tencent.com/document/pr…

1.安裝依賴

// IM 小程序 SDK
npm install tim-wx-sdk --save
// 發送圖片、文件等消息需要(yào / yāo)騰訊雲 即時(shí)通信 IM 上(shàng)傳插件
npm install tim-upload-plugin --save
複制代碼

2.在(zài)項目腳本裏引入模塊,并初始化。

安裝完依賴以(yǐ)後在(zài)項目目錄的(de)utils目錄下創建tencentIM目錄,用于(yú)存放關于(yú)即時(shí)通訊IM相關的(de)js文件。

封裝event.js

因爲(wéi / wèi)項目中需要(yào / yāo)一些時(shí)間監聽,所以(yǐ)需要(yào / yāo)在(zài)utils裏面添加一個(gè)event類,然後在(zài)app.js上(shàng)面綁定到(dào)wx全局對象上(shàng)。

// app.js
import Event from './utils/tencentIM/event'
wx.event = new Event();

// event.js
class Event {
  version = "1.0.0";
  constructor() {
    this._events = {};
  }
  on(eventName, listener) {
    if (!eventName || !listener) return;
    // 判斷回調的(de) listener 是(shì)否爲(wéi / wèi)函數
    if (isValidListener(listener)) {
      throw new TypeError("listener must be a function");
    }
    let events = this._events;
    let listeners = (events[eventName] = events[eventName] || []);
    let listenerIsWrapped = typeof listener === "object";
    // 不(bù)重複添加事件,判斷是(shì)否有一樣的(de)
    if (indexOf(listeners, listener) === -1) {
      listeners.push(
        listenerIsWrapped
          ? listeners
          : {
              listener,
              once: false,
            }
      );
    }
    return this;
  }
  once(eventName, listener) {
    // 直接調用 on 方法,once 參數傳入 true,待執行之(zhī)後進行 once 處理
    this.on(eventName, {
      listener,
      once: true,
    });
    return this;
  }
  emit(eventName, args) {
    // 直接通過内部對象獲取對應自定義事件的(de)回調函數
    let listeners = this._events[eventName];
    if (!listeners) return;
    // 需要(yào / yāo)考慮多個(gè) listener 的(de)情況
    listeners.forEach((listener) => {
      if (listener) {
        listener.listener.apply(this, args || []);
        if (listener.once) {
          this.off(eventName, listener.listener);
        }
      }
    });
    return this;
  }
  off(eventName, listener) {
    let listeners = this._events[eventName];
    if (!listener) return;
    listeners.some((item, index) => {
      if (item && item.listener === listener) {
        listeners.splice(index, 1);
        return true;
      }
      return false;
    });
  }
  allOff(eventName) {
    if (eventName && this._events[eventName]) {
      this._events[eventName] = [];
    } else {
      this._events = {};
    }
  }
}

// 判斷是(shì)否是(shì)合法的(de) listener
function isValidListener(listener) {
  if (typeof listener === "function") {
    return true;
  } else if (listener && typeof listener === "object") {
    return isValidListener(listener.listener);
  } else {
    return false;
  }
}

// 判斷新增自定義事件是(shì)否存在(zài)
function indexOf(array, item) {
  var result = -1;
  item = typeof item === "object" ? item.listener : item;
  for (var i = 0, len = array.length; i < len; i++) {
    if (array[i].listener === item) {
      result = i;
      break;
    }
  }
  return result;
}

複制代碼

服務端開發生成userSig接口或者下載騰訊雲提供生成userSig的(de)文件

相關文檔: cloud.tencent.com/document/pr…

騰訊雲提供的(de)客戶端生成userSig文件: github.com/tencentyun/…

初始化tim并設置監聽

import TIM from 'tim-wx-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
let options = {
 SDKAppID: 0 // 接入時(shí)需要(yào / yāo)将0替換爲(wéi / wèi)您的(de)即時(shí)通信 IM 應用的(de) SDKAppID
};
// 創建 SDK 實例,`TIM.create()`方法對于(yú)同一個(gè) `SDKAppID` 隻會返回同一份實例
let tim = TIM.create(options); // SDK 實例通常用 tim 表示
// 設置 SDK 日志輸出(chū)級别,詳細分級請參見 <a href="https://imsdk-1252463788.file.myqcloud.com/IM_DOC/Web/SDK.html#setLogLevel">setLogLevel 接口的(de)說(shuō)明</a>
tim.setLogLevel(0); // 普通級别,日志量較多,接入時(shí)建議使用
// tim.setLogLevel(1); // release 級别,SDK 輸出(chū)關鍵信息,生産環境時(shí)建議使用
// 注冊騰訊雲即時(shí)通信 IM 上(shàng)傳插件
tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
// 監聽事件,例如:
tim.on(TIM.EVENT.SDK_READY, function(event) {
// 收到(dào)離線消息和(hé / huò)會話列表同步完畢通知,接入側可以(yǐ)調用 sendMessage 等需要(yào / yāo)鑒權的(de)接口
// event.name - TIM.EVENT.SDK_READY
});
tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event) {
// 收到(dào)推送的(de)單聊、群聊、群提示、群系統通知的(de)新消息,可通過遍曆 event.data 獲取消息列表數據并渲染到(dào)頁面
// event.name - TIM.EVENT.MESSAGE_RECEIVED
// event.data - 存儲 Message 對象的(de)數組 - [Message]
});
tim.on(TIM.EVENT.MESSAGE_REVOKED, function(event) {
// 收到(dào)消息被撤回的(de)通知
// event.name - TIM.EVENT.MESSAGE_REVOKED
// event.data - 存儲 Message 對象的(de)數組 - [Message] - 每個(gè) Message 對象的(de) isRevoked 屬性值爲(wéi / wèi) true
});
tim.on(TIM.EVENT.MESSAGE_READ_BY_PEER, function(event) {
// SDK 收到(dào)對端已讀消息的(de)通知,即已讀回執。使用前需要(yào / yāo)将 SDK 版本升級至 v2.7.0 或以(yǐ)上(shàng)。僅支持單聊會話。
// event.name - TIM.EVENT.MESSAGE_READ_BY_PEER
// event.data - event.data - 存儲 Message 對象的(de)數組 - [Message] - 每個(gè) Message 對象的(de) isPeerRead 屬性值爲(wéi / wèi) true
});
tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function(event) {
// 收到(dào)會話列表更新通知,可通過遍曆 event.data 獲取會話列表數據并渲染到(dào)頁面
// event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
// event.data - 存儲 Conversation 對象的(de)數組 - [Conversation]
});
tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function(event) {
// 收到(dào)群組列表更新通知,可通過遍曆 event.data 獲取群組列表數據并渲染到(dào)頁面
// event.name - TIM.EVENT.GROUP_LIST_UPDATED
// event.data - 存儲 Group 對象的(de)數組 - [Group]
});
tim.on(TIM.EVENT.PROFILE_UPDATED, function(event) {
// 收到(dào)自己或好友的(de)資料變更通知
// event.name - TIM.EVENT.PROFILE_UPDATED
// event.data - 存儲 Profile 對象的(de)數組 - [Profile]
});
tim.on(TIM.EVENT.BLACKLIST_UPDATED, function(event) {
// 收到(dào)黑名單列表更新通知
// event.name - TIM.EVENT.BLACKLIST_UPDATED
// event.data - 存儲 userID 的(de)數組 - [userID]
});
tim.on(TIM.EVENT.ERROR, function(event) {
// 收到(dào) SDK 發生錯誤通知,可以(yǐ)獲取錯誤碼和(hé / huò)錯誤信息
// event.name - TIM.EVENT.ERROR
// event.data.code - 錯誤碼
// event.data.message - 錯誤信息
});
tim.on(TIM.EVENT.SDK_NOT_READY, function(event) {
// 收到(dào) SDK 進入 not ready 狀态通知,此時(shí) SDK 無法正常工作
// event.name - TIM.EVENT.SDK_NOT_READY
});
tim.on(TIM.EVENT.KICKED_OUT, function(event) {
// 收到(dào)被踢下線通知
// event.name - TIM.EVENT.KICKED_OUT
// event.data.type - 被踢下線的(de)原因,例如:
//    - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多實例登錄被踢
//    - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多終端登錄被踢
//    - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 簽名過期被踢 (v2.4.0起支持)。 
});
tim.on(TIM.EVENT.NET_STATE_CHANGE, function(event) { 
//  網絡狀态發生改變(v2.5.0 起支持)。 
// event.name - TIM.EVENT.NET_STATE_CHANGE 
// event.data.state 當前網絡狀态,枚舉值及說(shuō)明如下: 
//     \- TIM.TYPES.NET_STATE_CONNECTED - 已接入網絡 
//     \- TIM.TYPES.NET_STATE_CONNECTING - 連接中。很可能遇到(dào)網絡抖動,SDK 在(zài)重試。接入側可根據此狀态提示“當前網絡不(bù)穩定”或“連接中” 
//    \- TIM.TYPES.NET_STATE_DISCONNECTED - 未接入網絡。接入側可根據此狀态提示“當前網絡不(bù)可用”。SDK 仍會繼續重試,若用戶網絡恢複,SDK 會自動同步消息  
});
// 開始登錄 
tim.login({userID: 'your userID', userSig: 'your userSig'});
複制代碼

聊天開發記錄點

(1) 表情開發

進入 www.oicqzone.com/tool/emoji/ 網站,把最後一列的(de)表情下載下來(lái) 當然也(yě)可以(yǐ)通過下面這(zhè)種方式把表情一下子(zǐ)copy下來(lái),然後存到(dào)一個(gè)js裏面 命令: Array.from(document.querySelectorAll('body > table > tbody > tr > td:nth-child(8)')).map(item=>item && item.innerText||'')

然後導入項目中就(jiù)可以(yǐ)使用,點擊表情的(de)時(shí)候将表情直接放到(dào)輸入框文本後面

selectEmoji(e){
    const {text} = e.currentTarget.dataset
    this.setData({
      colSendMsg:this.data.colSendMsg + text,
    })
  }
複制代碼

(2)發送語音功能

發送語音主要(yào / yāo)是(shì)調用 wx.createInnerAudioContext()方法來(lái)實現的(de) 主要(yào / yāo)細節在(zài)于(yú)錄音授權、長按錄音,錄音時(shí)間短于(yú)一定時(shí)間不(bù)予以(yǐ)發送并提示,可以(yǐ)通過上(shàng)滑的(de)方式取消發送。

錄音授權

// 點擊錄音按鈕
  record() {
    let that = this
    // canRecord變量是(shì)用于(yú)保存是(shì)否授權錄音功能的(de)狀态
    if (that.canRecord) {
      // 開始錄音
      that.beforeStartRecord()
      return
    }
    wx.authorize({
      scope: 'scope.record',
      success() {
        console.log("錄音授權成功");
        that.canRecord = true
        // 用戶已經同意小程序使用錄音功能
      },
      fail() {
        console.log("第一次錄音授權失敗");
        wx.showModal({
          title: '提示',
          content: '您未授權錄音,功能将無法使用',
          showCancel: true,
          confirmText: "授權",
          confirmColor: "#52a2d8",
          success: function (res) {
            if (res.confirm) {
              //确認則打開設置頁面(重點)
              wx.openSetting({
                success: (res) => {
                  console.log(res.authSetting);
                  if (!res.authSetting['scope.record']) {
                    //未設置錄音授權
                    wx.showModal({
                      title: '提示',
                      content: '您未授權錄音,功能将無法使用',
                      showCancel: false,
                      success: function (res) {},
                    })
                  } else {
                    //第二次才成功授權
                    that.canRecord = true
                    console.log("設置錄音授權成功");
                  }
                },
                fail: function () {
                  console.log("授權設置錄音失敗");
                }
              })
            } else if (res.cancel) {
              console.log("cancel");
            }
          },
          fail: function () {
            console.log("openfail");
          }
        })
      }
    })
  },
複制代碼

後面錄音相關的(de)流程很多博文上(shàng)都有,就(jiù)不(bù)再贅述,隻提供大(dà)家一個(gè)思考:

(3)頁面滾動到(dào)底部

聊天頁面很重要(yào / yāo)一點是(shì)要(yào / yāo)讓用戶看到(dào)最新的(de)信息,所以(yǐ)要(yào / yāo)顯示頁面的(de)最底部内容,我根據以(yǐ)下幾種情況顯示最底部内容:

  1. 第一次進入頁面
  2. 發送任意消息
  3. 監聽到(dào)最新的(de)消息(這(zhè)裏其實推薦值設置提醒)

具體部分代碼如下:

// wxml
<scroll-view class="chat-area" refresher-enabled="{{true}}" refresher-triggered="{{triggered}}"
  bindrefresherrefresh="onRefresh" scroll-into-view="{{ viewIndex }}" scroll-y="true" enable-flex="{{true}}"
  bindtap="onHideSendMore" bindtap="hideBottom" style="padding-bottom:{{InputBottom}}px">
  <!-- <view wx:if="{{showLoading}}" class="cu-load loading"></view> -->
  <view class="cu-chat">
    <view wx:for="{{ arrMsg }}" wx:key="ID">
      // 具體内容
    </view>
  </view>
</scroll-view>

// js - pageScrollToBottom
  // isBottom 是(shì)否滑動到(dào)底部
  // lastIndex 滑動到(dào)指定位置
  // arrMsg 消息列表
  pageScrollToBottom(isBottom,lastIndex = 0) {
    let index = null
    if (isBottom) {
      index = 'msg-' + (this.data.arrMsg.length -1);
    } else if(lastIndex) {
      index = 'msg-' + lastIndex;
    }else{
      index = 'msg-' + 0;
    }
    this.setData({
      viewIndex: index
    })
  },
複制代碼

(4)底部彈起高度

我們在(zài)開發的(de)時(shí)候常常需要(yào / yāo)底部彈起,還有鍵盤彈起的(de)操作,這(zhè)裏也(yě)需要(yào / yāo)優化一下用戶的(de)體驗。

  1. 彈起的(de)時(shí)候讓聊天記錄也(yě)向上(shàng)推起
  2. 保持其他(tā)部門彈起的(de)高度和(hé / huò)鍵盤彈起高度一樣(這(zhè)樣就(jiù)不(bù)會有過多的(de)切換跳動效果)

我使用 style="padding-bottom:{{InputBottom}}px" 的(de)方式,inputBottom默認我設置了(le/liǎo)200,然後在(zài)鍵盤彈起的(de)時(shí)候記錄鍵盤彈起的(de)高度,然後保存鍵盤彈起的(de)高度,下次使用這(zhè)個(gè)保存的(de)高度

// wxml
<input catchblur="InputBlur"></input>
// js
InputFocus(e) {
    let height = e.detail.height
    if(height > 0) this.InputBottom = height
    this.setData({
      InputBottom: height
    })
}
複制代碼

尾聲

本文隻是(shì)對我開發這(zhè)個(gè)項目的(de)時(shí)候一些重要(yào / yāo)的(de)地(dì / de)方的(de)一個(gè)記錄,供大(dà)家參考和(hé / huò)以(yǐ)後個(gè)人(rén)的(de)回顧,如果有不(bù)足的(de)地(dì / de)方,還請大(dà)家多多指點。

相關案例查看更多