欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

微信小程序即時(shí)通訊開(kāi)發(fā)記錄(結(jié)合通訊云IM) - 新聞資訊 - 云南小程序開(kāi)發(fā)|云南軟件開(kāi)發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開(kāi)發(fā)/軟件開(kāi)發(fā)

知識(shí)

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X(jué)表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷(xiāo)的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷(xiāo)工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!

您當(dāng)前位置>首頁(yè) » 新聞資訊 » 小程序相關(guān) >

微信小程序即時(shí)通訊開(kāi)發(fā)記錄(結(jié)合通訊云IM)

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

發(fā)布人:葵宇科技

瀏覽次數(shù):121

前言

最近接到一個(gè)需求是在原項(xiàng)目的基礎(chǔ)上去開(kāi)發(fā)一個(gè)即時(shí)通訊的功能,主要是一對(duì)一的聊天,供客戶(hù)和客戶(hù)之間進(jìn)行即時(shí)通訊,功能主要包括聊天列表、發(fā)送文字、發(fā)送表情、發(fā)送圖片、發(fā)送視頻、發(fā)送語(yǔ)音、發(fā)送自定義信息等功能。因?yàn)轫?xiàng)目需求,所以選擇了騰訊云IM來(lái)開(kāi)發(fā)。

開(kāi)發(fā)流程

首先是要注冊(cè)騰訊云并創(chuàng)建應(yīng)用,拿到APPID和秘鑰。

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

1.安裝依賴(lài)

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

2.在項(xiàng)目腳本里引入模塊,并初始化。

安裝完依賴(lài)以后在項(xiàng)目目錄的utils目錄下創(chuàng)建tencentIM目錄,用于存放關(guān)于即時(shí)通訊IM相關(guān)的js文件。

封裝event.js

因?yàn)轫?xiàng)目中需要一些時(shí)間監(jiān)聽(tīng),所以需要在utils里面添加一個(gè)event類(lèi),然后在app.js上面綁定到wx全局對(duì)象上。

// 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;
    // 判斷回調(diào)的 listener 是否為函數(shù)
    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";
    // 不重復(fù)添加事件,判斷是否有一樣的
    if (indexOf(listeners, listener) === -1) {
      listeners.push(
        listenerIsWrapped
          ? listeners
          : {
              listener,
              once: false,
            }
      );
    }
    return this;
  }
  once(eventName, listener) {
    // 直接調(diào)用 on 方法,once 參數(shù)傳入 true,待執(zhí)行之后進(jìn)行 once 處理
    this.on(eventName, {
      listener,
      once: true,
    });
    return this;
  }
  emit(eventName, args) {
    // 直接通過(guò)內(nèi)部對(duì)象獲取對(duì)應(yīng)自定義事件的回調(diào)函數(shù)
    let listeners = this._events[eventName];
    if (!listeners) return;
    // 需要考慮多個(gè) listener 的情況
    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 = {};
    }
  }
}

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

// 判斷新增自定義事件是否存在
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;
}

復(fù)制代碼

服務(wù)端開(kāi)發(fā)生成userSig接口或者下載騰訊云提供生成userSig的文件

相關(guān)文檔: cloud.tencent.com/document/pr…

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

初始化tim并設(shè)置監(jiān)聽(tīng)

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

聊天開(kāi)發(fā)記錄點(diǎn)

(1) 表情開(kāi)發(fā)

進(jìn)入 www.oicqzone.com/tool/emoji/ 網(wǎng)站,把最后一列的表情下載下來(lái) 當(dāng)然也可以通過(guò)下面這種方式把表情一下子copy下來(lái),然后存到一個(gè)js里面 命令: Array.from(document.querySelectorAll('body > table > tbody > tr > td:nth-child(8)')).map(item=>item && item.innerText||'')

然后導(dǎo)入項(xiàng)目中就可以使用,點(diǎn)擊表情的時(shí)候?qū)⒈砬橹苯臃诺捷斎肟蛭谋竞竺?/p>

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

(2)發(fā)送語(yǔ)音功能

發(fā)送語(yǔ)音主要是調(diào)用 wx.createInnerAudioContext()方法來(lái)實(shí)現(xiàn)的 主要細(xì)節(jié)在于錄音授權(quán)、長(zhǎng)按錄音,錄音時(shí)間短于一定時(shí)間不予以發(fā)送并提示,可以通過(guò)上滑的方式取消發(fā)送。

錄音授權(quán)

// 點(diǎn)擊錄音按鈕
  record() {
    let that = this
    // canRecord變量是用于保存是否授權(quán)錄音功能的狀態(tài)
    if (that.canRecord) {
      // 開(kāi)始錄音
      that.beforeStartRecord()
      return
    }
    wx.authorize({
      scope: 'scope.record',
      success() {
        console.log("錄音授權(quán)成功");
        that.canRecord = true
        // 用戶(hù)已經(jīng)同意小程序使用錄音功能
      },
      fail() {
        console.log("第一次錄音授權(quán)失敗");
        wx.showModal({
          title: '提示',
          content: '您未授權(quán)錄音,功能將無(wú)法使用',
          showCancel: true,
          confirmText: "授權(quán)",
          confirmColor: "#52a2d8",
          success: function (res) {
            if (res.confirm) {
              //確認(rèn)則打開(kāi)設(shè)置頁(yè)面(重點(diǎn))
              wx.openSetting({
                success: (res) => {
                  console.log(res.authSetting);
                  if (!res.authSetting['scope.record']) {
                    //未設(shè)置錄音授權(quán)
                    wx.showModal({
                      title: '提示',
                      content: '您未授權(quán)錄音,功能將無(wú)法使用',
                      showCancel: false,
                      success: function (res) {},
                    })
                  } else {
                    //第二次才成功授權(quán)
                    that.canRecord = true
                    console.log("設(shè)置錄音授權(quán)成功");
                  }
                },
                fail: function () {
                  console.log("授權(quán)設(shè)置錄音失敗");
                }
              })
            } else if (res.cancel) {
              console.log("cancel");
            }
          },
          fail: function () {
            console.log("openfail");
          }
        })
      }
    })
  },
復(fù)制代碼

后面錄音相關(guān)的流程很多博文上都有,就不再贅述,只提供大家一個(gè)思考:

(3)頁(yè)面滾動(dòng)到底部

聊天頁(yè)面很重要一點(diǎn)是要讓用戶(hù)看到最新的信息,所以要顯示頁(yè)面的最底部?jī)?nèi)容,我根據(jù)以下幾種情況顯示最底部?jī)?nèi)容:

  1. 第一次進(jìn)入頁(yè)面
  2. 發(fā)送任意消息
  3. 監(jiān)聽(tīng)到最新的消息(這里其實(shí)推薦值設(shè)置提醒)

具體部分代碼如下:

// 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">
      // 具體內(nèi)容
    </view>
  </view>
</scroll-view>

// js - pageScrollToBottom
  // isBottom 是否滑動(dòng)到底部
  // lastIndex 滑動(dòng)到指定位置
  // 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
    })
  },
復(fù)制代碼

(4)底部彈起高度

我們?cè)陂_(kāi)發(fā)的時(shí)候常常需要底部彈起,還有鍵盤(pán)彈起的操作,這里也需要優(yōu)化一下用戶(hù)的體驗(yàn)。

  1. 彈起的時(shí)候讓聊天記錄也向上推起
  2. 保持其他部門(mén)彈起的高度和鍵盤(pán)彈起高度一樣(這樣就不會(huì)有過(guò)多的切換跳動(dòng)效果)

我使用 style="padding-bottom:{{InputBottom}}px" 的方式,inputBottom默認(rèn)我設(shè)置了200,然后在鍵盤(pán)彈起的時(shí)候記錄鍵盤(pán)彈起的高度,然后保存鍵盤(pán)彈起的高度,下次使用這個(gè)保存的高度

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

尾聲

本文只是對(duì)我開(kāi)發(fā)這個(gè)項(xiàng)目的時(shí)候一些重要的地方的一個(gè)記錄,供大家參考和以后個(gè)人的回顧,如果有不足的地方,還請(qǐng)大家多多指點(diǎn)。

相關(guān)案例查看更多