六、Uniapp+vue+騰訊IM+騰訊音影片開發仿微信的IM聊天APP,支援各類消息收發,音影片通話,附vue實現源碼(已開源)-聊天介面容器的實現

會話好友列表的實現

1、項目引言
2、騰訊雲後台配置TXIM
3、配置項目並實現IM登錄
4、會話好友列表的實現
5、聊天輸入框的實現
6、聊天介面容器的實現
7、聊天消息項的實現
8、聊天輸入框擴展面板的實現
9、聊天會話管理的實現
10、聊天記錄的載入與消息收發
11、定位SD配置與收發定位消息
12、貼圖表情的訂製化開發
13、騰訊雲後台配置TRTC功能
14、集成音影片通話功能
15、集成仿微信的拍照,相冊選擇插件
16、集成美顏功能
17、集成TPNS消息推送(暫未接入)


@


文章概述

上一次我們討論過聊天輸入框的實現,實現文本,語音,標籤的編輯發送,而本次我們的關注點將回歸到整個聊天介面容器的實現。


聊天介面容器的實現

1.為什麼要實現一個聊天介面容器

一般情況下,開發者會採用直接在對應的介面中實現聊天介面UI的方式做開發。這種開發思路無可厚非,但是如果我們要實現跨平台的聊天IM,那麼我們不免要遇到組件之間的事件衝突,多平台表現差距的問題,特別是在聊天中我們還需要實現滾動載入。如果說我們在每個介面都實現了一套程式碼,那麼會導致整體程式碼的可維護性下降,因此我們有必要實現一個聊天介面容器,將消息顯示,滾動載入,長按菜單,輸入,編輯等都在這裡面做好兼容處理,使得介面和業務組件實現解耦合。
在這裡插入圖片描述

2. 聊天介面容器的設計

聊天介面容器的設計很簡單,我們大概整理一下需要實現以下幾點功能。

  • 滾動到底部
  • 輸入法高度自適應
  • 滾動載入

2.1 滾動到底部

首先我們需要明確我們的聊天介面肯定是需要滾動的,並且在接收/發送消息的時候我們需要滾動在最底部,類似微信收發消息的效果,組件我們肯定只有scrollView可以使用,具體實現如下

<scroll-view
      ref="scroll"
      class="chat-layout__scroll"
      :style="{
        height: scrollBoxHeight
      }"
      :scroll-y="!inUpperLoading"
      :upper-threshold="0"
      :show-scrollbar="false"
      :scroll-top="scrollTop"
      @scrolltoupper="onScrollToUpper"
      @click="onScrollClick"
      @scroll="onScroll"
    >
      <text
        v-if="inUpperLoading && !end"
        class="chat-layout__scroll-loading"
      >
      </text>
      <slot></slot>
      <view ref="ending"></view>
    </scroll-view>

實現滾動到底部,一般而言是只能使用scrollTop,而這樣為了兼容nvue,我們採用weex原生的dom模組獲取可滾動高度 – 容器高度就可以實現可滾動高度。

      async scrollToBottom (retouchCount) {
        if (this.inUpperLoading || this.scrollBottoming) {
          return
        }
        this.scrollBottoming = true
        // await this.$nextTick()
        // this.$utils.delay(100)
        // let view = await this.$utils.query(this, '.chat-layout__scroll')
        // this.scrollTop = view.scrollHeight

        // nvue必須使用下面這種
        await this.$nextTick()
        dom.scrollToElement(this.$refs.ending, { animated: false })
        this.scrollBottoming = false
        await this.$nextTick()
        retouchCount = retouchCount || 0
        retouchCount++
        // 最多5次重新滾動到底部的測試, nvue下面的渲染並且是nextTick之後就百分百正常
        dom.getComponentRect(this.$refs.scroll, ({ size }) => {
          let { detail } = this.scrollEvent
          if (!detail) {
            this.scrollTop = 0
            if (retouchCount < 5) {
              this.scrollToBottom(retouchCount)
            }
          } else {
            this.scrollTop = detail.scrollHeight - size.height
            // console.log('重新定位scrollTop')
            if (retouchCount < 5) {
              this.scrollToBottom(retouchCount)
            }
          }
        })
      }

2.2輸入法高度自適應

一般情況下在vue介面,我們input focus之後是會自動適應輸入法高度,然而在nvue中,當我們介面中嵌套了影片播放器之類的,會導致input高度計算錯誤,因此我們需要手動監聽輸入法高度變化,然後做一個適應性的收縮介面容器即可。

<view
    class="chat-layout"
    :style="{
      paddingBottom: paddingBottomHeight
    }"
  >
  </view>
uni.onKeyboardHeightChange(this.onKeyboardHeightChange)

// methods中
async onKeyboardHeightChange ({ height }) {
  this.paddingBottomHeight = height
  await this.$nextTick()
  height && this.scrollToBottom()
},

2.3滾動載入

滾動載入有很多方式實現,這裡我們只簡易的實現了一個,監聽scrollView的scrolltoupper事件確定是否載入,然後我們拋出給業務層控制滾動載入。

    <scroll-view
      ref="scroll"
      class="chat-layout__scroll"
      :style="{
        height: scrollBoxHeight
      }"
      :scroll-y="!inUpperLoading"
      :upper-threshold="0"
      :show-scrollbar="false"
      :scroll-top="scrollTop"
      @scrolltoupper="onScrollToUpper"
      @click="onScrollClick"
      @scroll="onScroll"
    >
onScrollToUpper: throttle(async function() {
        if (this.inUpperLoading || this.disableUpperLoading || this.end) {
          return
        }
        this.inUpperLoading = true
        let oldChildrens = this.$refs.scroll.children.length
        this.$emit('upperLoading', async isEnd => {
          if (isEnd) {
            this.disableUpperLoading = true
          }
          await this.$nextTick()
          let newChildrens = this.$refs.scroll.children.length
          this.inUpperLoading = false
          let refEl = this.$refs.scroll.children[newChildrens - oldChildrens]
          dom.scrollToElement(refEl, { animated: false })
        })
      }, 200, {
        leading: true,
        trailing: false
      }),

而對於業務層來說,接收到upperLoading事件之後做數據載入,然後回饋載入完成即可

async loadMoreLog (next) {
  console.log('loadMoreLog')
  await this.getLogs(this.screenLogLen)
  await this.$nextTick()
  next(false)
},

項目開源地址及交流群

項目開源地址://gitee.com/ckong/Zhimi.OpenSource.UniApp.TXIM.Vue
Uniapp開發交流群:755910061