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 は、元のデータと同じデータがある場合、更新ステートメントを再度実行しますか?

推薦する

JS 継承の詳細

目次序文準備する要約する継承方法プロトタイプ継承プロトタイプチェーン継承コンストラクタの借用(クラス...

MySQL データベースの操作とデータ型

目次1. データベース操作1.1 データベースの表示1.2 データベースを作成する1.3 データベー...

Vue の v-model ディレクティブと .sync 修飾子の違いの詳細な説明

目次vモデル.sync微妙な違い機能シナリオを要約します。 vモデル <!--親コンポーネント...

mysql 8.0.18.zip のインストールと構成方法のグラフィック チュートリアル (Windows 64 ビット)

以前にインストールされたバージョンのデータベースをアンインストールする方法については、この記事を参照...

複数クリックを防ぐVueの実践

通常、クリック イベントは、メッセージ リマインダーのさまざまな状況に分割されます。これらが処理され...

JavaScript はドラッグ可能なモーダルボックスを実装します

この記事では、ドラッグ可能なモーダルボックスを実装するためのJavaScriptの具体的なコードを参...

Gitlab-ci を使用してリモート マシンに継続的にデプロイする方法 (詳細なチュートリアル)

簡単に言うと、今日は Gitlab-CI を使用してリモート サーバーに自動的にデプロイする方法につ...

Vueは単純なランダムロールコールを実行します

目次レイアウト部分: <div id="アプリ"> <p>...

JS で列挙をシミュレートする方法

序文現在の JavaScript には列挙の概念がありません。一部のシナリオでは、列挙を使用するとデ...

vue-routerのマッチングに基づいてパンくずリスト機能を実現する

この記事では主にvue-routerのmatchedをベースにしたbreadcrumb機能を紹介し、...

HTMLの表のtbodyは上下左右にスライドできます

テーブル ヘッダーが固定されている場合は、それを 2 つのテーブルに分割する必要があります。1 つの...

MySQL 8.0.12 解凍版インストールチュートリアル個人テスト!

Mysql8.0.12 解凍版のインストール方法をテストしましたので、ご参考までに1. ダウンロー...

Navicat for MySql ビジュアルインポート CSV ファイル

この記事では、参考までに、Navicat for MySql の CSV ファイルのビジュアルインポ...

Linux サービスでファイアウォールを有効にする 2 つの方法

方法は2つあります: 1. サービス方法ファイアウォールのステータスを確認します。 [root@ce...

Win10環境にMysql5.7.23をインストールする際の問題点と落とし穴

たくさんのチュートリアルを読みましたが、うまくインストールできませんでした。しばらく試行錯誤した後、...