Vue が Web オンラインチャット機能を実現

Vue が Web オンラインチャット機能を実現

この記事では、Webオンラインチャットを実装するためのVueの具体的なコードを参考までに紹介します。具体的な内容は次のとおりです。

最終的な効果

実装プロセス

無限スクロールフォームの実装については、以前にも紹介したので、ここでは繰り返しません。よくわからない場合は、前のドキュメントのポータルから確認できます。

リアルタイムオンラインチャットの主な機能

  • 2 日間のウィンドウの上部までスクロールすると、履歴情報やその他の情報が自動的に読み込まれます。データの読み込み中は、読み込みアニメーションが必要です。
  • メッセージを送信すると、スクロールバーが自動的にウィンドウの下部にスライドし、送信したメッセージがチャット ウィンドウに表示されます。
  • 他の人からメッセージを受信したときに、ウィンドウ内のスクロールバーの位置を決定する必要があります。メッセージが下から一定の範囲内で受信された場合、自動的にウィンドウの下部にスライドする必要があります。
  • 送受信したメッセージをチャットステータスに繰り返し表示することはできません。
  • 送信されたメッセージと受信されたメッセージは、チャット ウィンドウに逆の順序で表示される必要があります。つまり、ウィンドウの下部に近いメッセージが最新のメッセージになります。
  • 認証のためには、WebSocket を介してバックエンドと長い接続を確立するのが最適です。新しいメッセージがある場合、バックエンドは積極的にメッセージをフロントエンドにプッシュします。ここでは、フロントエンドにチャットウィンドウを実装するというアイデアを主に紹介し、WebSocket 部分は拡張しません。タイマーポーリングによって単純に実装されます。

さっそくコードを見てみましょう

バックエンドの戻りデータ形式

すべての設計と機能実装はデータに基づいていると思うので、まずはバックエンドから返されるデータ形式を見てみましょう。

{
 "code": 200, // 応答コード "msg": "OK", // 応答メッセージ "total": 1, 
 "sysTime": "2020-12-16 15:23:27", // システム応答時間 "data": [{
  "avatar": "", // ユーザーアバター "content": "{\"type\":\"txt\",\"msg\":\"こんにちは! \"}", // メッセージの内容"isRead": 0, // 既読ですか? "isOneself": 0, // 自分から送信されたメッセージですか? 0 は未読、1 は既読"msgId": 10, // メッセージ ID、重複排除に使用"nickName": "碧海燕鱼", // ユーザーのニックネーム"userCode": "202012162030202232" // ユーザー コード}]
}

ここで注目すべきは、コンテンツ フィールドは JSON 形式の文字列データを返すことであり、コンテンツの形式は次のようになります。

// テキストメッセージ {
  "タイプ": "txt",
  "msg":"Hello" //メッセージの内容}
// 画像メッセージ {
  "タイプ": "画像",
  "url": "画像アドレス",
  "拡張子":"jpg",
  "幅":360、//幅"高さ":480、//高さ"サイズ": 388245
}
// ビデオメッセージ {
  「タイプ」: 'ビデオ',
  「URL」: "http://nimtest.nos.netease.com/cbc500e8-e19c-4b0f-834b-c32d4dc1075e",
  "拡張子":"mp4",
  "幅":360、//幅"高さ":480、//高さ"サイズ": 388245
}
// 場所メッセージ {
  "タイプ": "ローカル",
  "address":"No. 599, Wangshang Road, Hangzhou, Zhejiang, China", //地理的位置 "longitude":120.1908686708565, // 経度 "latitude":30.18704515647036 // 緯度}

HTMLコード

<テンプレート>
  <Modal title="オンラインコミュニケーション" v-model="chatVisible"
   ドラッグ可能
   フッターを非表示
   :width="580" @on-cancel="キャンセル">
   <div class="チャット">
     <div class="チャットメッセージ本文" id="チャットフォーム" @scroll="スクロール"
      >
      <スピンv-if="読み込み中">
        <アイコン type="ios-loading" size=18 class="spin-icon-load"></アイコン>
      </スピン>
        <div dis-hover v-for="(item,index) in data"
         :key="index" class="メッセージカード">
         <div :class="item.isOneself == 1?'メッセージ行右':'メッセージ行左'">
           <img :src="item.avatar?item.avatar:defualtアバター" 
            高さ="35" 幅="35" >
            <div class="メッセージコンテンツ"> 
              <div :style="item.isOneself == 1?'text-align:right;display: flex;flex-direction:row-reverse':''">
                {{item.ニックネーム}}
                <span class="メッセージ時間">
                   {{item.createTime}} </span>
                </div>
              <div class="メッセージ本文">
                {{item.content.msg}}
                </div>
             </div> 
          </div>
         </div>
      </div>
        <入力
        v-model="フォーム.msg"
        タイプ="テキストエリア"
        スタイル="margin:10px 0;"
        placeholder="もっと積極的に行動すれば、世界はもっと広がります!"
        :行数="4"
      />
     </div>
     <div class="footer-btn">
        <Button @click="cancel" type="text">キャンセル</Button>
        <Button type="primary" @click="sendMsg">送信</Button>
      </div>
  </モーダル>
</テンプレート>

注意:自分と他人が送信したメッセージの表示スタイルは異なるため、isOneself フィールドを使用して表示スタイルを区別する必要があります。

JavaScript コード

<スクリプト>
"@/api/index" から {listMsg,sendMsg } をインポートします。
エクスポートデフォルト{
  名前:「チャット」、
  小道具: {
    価値: {
      タイプ: ブール値、
      デフォルト: false
    }
  },
  データ() {
    戻る {
      チャット可視:this.value、
      読み込み中:false、
      defualtAvatar:require('../../assets/defult-avatar.svg'), // バックエンドはデフォルトのアバターを返しません。注: ローカルファイル data:[] に動的にアクセスするには、require リクエストメソッドが必要です。
      distincData:[], // メッセージ重複排除配列 offsetMax:0, // 最大オフセット、現在の最大 ID を記録し、将来スケジュールされた時間にデータをポーリングするたびにこの ID より大きいデータのみを取得します offsetMin:0, // 最小オフセット、現在の最小 ID を記録し、上にスライドするたびにこの ID より大きいデータのみを取得します searchForm:{ // データが取得または初めて読み込まれるたびに送信されるフォームデータ pageNumber: 1,
        ページサイズ: 20
      },
      form:{ // データを送信するフォームコンテンツ:"",
        メッセージ:""
      },
      timerSwitch:0 // タイマースイッチ、デフォルトでは閉じています};
  },
  メソッド: {
    初期化(){
      
    },
    loadMsg(){ // デフォルトではフォームが開き、データのページが読み込まれます。フォームは一定期間に 1 回実行されます。let that = this;
      this.searchForm.offsetMax = this.offsetMax;
      listMsg(this.searchForm).then(res=>{
        (res.code == 200)の場合{
          res.data.forEach(e => {
            // 最大オフセットをマークします if (that.offsetMax < e.msgId) {
                that.offsetMax = e.msgId;
            }
            JSON を解析します。
            that.data.unshift(e)
            that.distincData.push(e.msgId);
            // 最大オフセットをマークします。バックエンドから返されるデータは逆順なので、最後の ID が最新です。that.offsetMin = e.msgId;
           });
          // データの読み込みが完了すると、スクロール バーがフォームの一番下までスクロールします。this.scrollToBottom();
        }
      });
       
        
    },
    show(){ //フォームを開いてデータを初期化します //データを初期化します this.data = [];
      this.distincData = [];
      this.offsetMax = 0;
      this.offsetMin = 0;
      this.searchForm.pageNumber = 1;
      this.searchForm.pageSize = 20;
      this.form = {
        コンテンツ:""、
        メッセージ:""
      };
      このメソッドは、
      this.chatVisible = true;
      // タイマーをオンにします this.timerSwitch = 1;
      これを再ロードします。
    },
    sendMsg(){ // メッセージを送信 if(!this.form.msg){
         this.$Message.warning("空のメッセージを送信できません");
        戻る;
      }
      let content = { // メッセージ本文をカプセル化 type:"txt",
        メッセージ:このフォームのメッセージ
      }; 
      this.form.content = JSON.stringify(content);
      sendOrderMsg(this.form).then(res=>{
        (res.code == 200)の場合{
          データを JSON にパースします。
          this.data.push(res.data)
          このフォームのmsg="";
          this.distincData.push(res.data.msgId);
          スクロールダウン
          // メッセージを送信すると、現在のメッセージのみが返されます。相手側がすでにメッセージを送信している可能性があるため、オフセットは変更されません。}
      });
    },
    scrollToBottom(){ //フォームの一番下までスクロールします this.$nextTick(()=>{
          チャットフォームを document.getElementById("チャットフォーム");
          チャットフォームのスクロールトップ = チャットフォームのスクロール高さ;
      });
    },
    // 一番上までスクロールし、ページング パラメータに従って履歴データを取得します。オフセットマークを変更する必要はありませんが、再スクロールを判断する必要があります(){
      チャットフォームを document.getElementById("チャットフォーム");
      scrollTop を chatform.scrollTop とします。
      スクロールトップ == 0 の場合
        this.loading = true; です。
        that = this とする;
        this.searchForm.offsetMin = this.offsetMin;
        this.searchForm.offsetMax = "";
        listMsgByOrder(this.searchForm).then(res=>{
           this.loading = false; です。
            (res.code == 200)の場合{
              res.data.forEach(e => {
                if (that.distincData.indexOf(e.msgId) < 0) {
                  JSON を解析します。
                  that.data.unshift(e);
                  that.distincData.push(e.msgId);
                  // 最小オフセットを変更する if (that.offsetMin > e.msgId) {
                      that.offsetMin = e.msgId;
                  }
                }
              });
            }
        });
      }
    },
   リロードデータ(){
    // タイマースイッチがオンかどうかを判断します。オンの場合はタイマーを実行します。if(this.timerSwitch){
      タイムアウトを設定する(() => {
        パラメータを {} とします。
        パラメータ.ページ番号 = 1;
        パラメータ.pageSize = 20;
        パラメータのオフセットMax = this.offsetMax;
        that = this とする;
        listMsgByOrder(params).then(res=>{
          (res.code == 200)の場合{
            res.data.forEach(e => {
              // 最大オフセットを変更し、重複チェックの前に配置して、現在のメッセージがメッセージリストに追加されるのを防ぎますが、オフセット値はそこにありません if (that.offsetMax < e.msgId) {
                  that.offsetMax = e.msgId;
              }
              if (that.distincData.indexOf(e.msgId) < 0) {
                JSON を解析します。
                that.data.push(e)
                that.distincData.push(e.msgId);
                // 新しいメッセージを受信し、高さを決定します。現在のスクロール バーの高さが下から 100 未満の場合、一番下までスライドします。let chatform = document.getElementById("chatform");
                ギャップをchatform.scrollHeight -chatform.scrollTopとします。
                if(ギャップ >0 && ギャップ < 400){
                  スクロールダウン
                }
              }
            });
            that.reloadData();
          }
        });
      },1000*2);
    }
    
   },
   cancel(){ // フォームを閉じるには、プロンプトタスクスイッチもオフにする必要があります。 this.chatVisible = false;
     this.timerSwitch = 0;
   }
  },
  マウント() {
  }
};
</スクリプト>

CSSコード

<スタイル lang="less">
   。メッセージ {
        高さ: 350ピクセル;
    }
  .ivu-card-body {
    パディング:5px;
  }
  .ivu-modal-body{
    パディング: 0px 16px 16px 16px;
  }
  .チャットメッセージ本文{
   背景色:#F8F8F6;
   幅:545px;
   高さ: 350ピクセル;
   オーバーフロー:自動;
  }
  .メッセージカード{
   マージン:5px;
  }
  .メッセージ行左 {
   ディスプレイ: フレックス;
   flex-direction:行;
  }
  .メッセージ行右 {
   ディスプレイ: フレックス;
   flex-direction:行の反転;
  }
  .メッセージコンテンツ{
    マージン:-5px 5px 5px 5px;
    ディスプレイ: フレックス;
    flex-direction:列;
  }
  .メッセージ本文{
    境界線:1px 実線 #D9DAD9;
    パディング:5px;
    境界線の半径:3px;
    背景色:#FFF;
  }
  .メッセージ時間{
    マージン:0 5px;
    フォントサイズ:5px;
    色:#D9DAD9;
  }
  .footer-btn {
    フロート:右;
    下部マージン: 5px;
  }
  .スピンアイコンロード{
    アニメーション:ani-spin 1s 線形無限;
  }
  @keyframes アニスピン{
    フォーム{transform:rotate(0deg);}
    50% {変換: 回転(180度);}
    {transform: rotate(360deg);} へ
  }
</スタイル>

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Vue+express+Socketでチャット機能を実現
  • Vueはチャットインターフェースを実装する
  • Vue+ウェブ端末がWeChatウェブ版チャットルーム機能を模倣
  • Vue.js は WeChat チャットウィンドウを模倣してコンポーネント機能を表示します
  • Vue + socket.io はシンプルなチャットルームのサンプルコードを実装します
  • Vue2ベースのモバイルQQを模倣したシングルページアプリケーション機能(チャットボットへのアクセス)
  • RongCloud IM を使用して Vue Cli 3 プロジェクトにチャット機能を実装する方法
  • Vue で実装された WeChat ロボット チャット機能の例 [ソース コードのダウンロードあり]
  • Vue と Websocket をベースにした複数人用オンライン チャット ルーム
  • オンラインチャットを実現するVue+sshフレームワーク

<<:  Centos での TCPWrappers アクセス制御の実装

>>:  MySQL は、元のデータと同じデータがある場合、更新ステートメントを再度実行しますか?

推薦する

ウェブページエクスペリエンス: 計画と設計

1. デザインの方向性を明確にする<br />まず、どのユーザーを対象にデザインするのか...

jsネイティブウォーターフォールフロープラグイン制作

この記事では、jsネイティブウォーターフォールフロープラグインの具体的なコードを参考までに共有します...

mini-vueレンダリングのシンプルな実装

目次序文ターゲット最初のステップ:ステップ2:ステップ3:ステップ4:要約する序文現在主流のフレーム...

Web面接におけるJS事前解析と変数プロモーションの違い

目次事前分析とは何ですか?変数と関数の準備の違いvar 変数の繰り返し宣言変数と関数の昇格の優先順位...

Mysql5.7 のルートパスワードを忘れた場合の対処法 (シンプルで効果的な方法)

前回の記事では、MySQL 5.7でルートパスワードを忘れた場合と、MySQL 5.7でルートパスワ...

MySQL 8.0 オンライン DDL クイック列追加の概要

目次問題の説明MySQLオンラインDDLで列を追加する従来の方法01 コピー方法02 インプレースメ...

mysql5.7.21.zip インストールチュートリアル

mysql5.7.21 zipの詳細なインストール手順は次のとおりです。 1. 解凍して指定されたデ...

Linux サーバー上の hosts ファイル構成の詳細な説明

Linux サーバーのホスト ファイルの構成hosts ファイルは、Linux システム内の IP ...

MySQLコマンドラインでSQLファイルを実行するいくつかの方法

目次最初の方法: MySQLデータベースが接続されていない場合2 番目の方法: データベースがすでに...

XHTML 入門チュートリアル: XHTML とは何ですか?

HTMLとは何ですか?簡単に言えば、HTML は Web ページを作成するために使用されます。とて...

Vue 2つのフィールドの共同検証によりパスワード変更機能を実現

目次1. はじめに2. ソリューションの実装2.1 実装コード2.2 コードの説明2.3 検証結果1...

「@INC で ExtUtils/MakeMaker.pm が見つかりません」というエラーを解決する

mha4mysql をインストールする場合の手順は、おおよそ次のようになります: unzip、per...

CSS3を使用してオンラインライブ放送に似たキューアニメーションを実装する方法

以前、グループの友人が質問しました。つまり、ミニプログラムでユーザーがオンラインになったときに、ライ...

mysql スケジュールタスク (イベント イベント) の詳細な説明

1. イベントの簡単な紹介イベントは、MySQL が特定の時間に呼び出す手続き型データベース オブジ...

nginx-ingress-controller ログ永続化ソリューションのソリューション

最近、nginx-ingress-controller のアプリケーションについて説明した公開アカウ...