Ionic—聊天机器人模块开发

一.聊天模块页面列表设计与开发

1.开发设计

  • 开发内容:开发聊天用户的列表布局与绑定

  • ion-item上使用click事件不是太好,故直接navPush属性设置点击跳转到的组件,通过navParam属性设置点击跳转组件时传递的参数

    <ion-item [navPush]="ChatdetailsPage" [navParam]="userInfo.userId">
    
  • 创建聊天详情页面ionic g page chatdetails

2.实例代码

  • chatbubbles.html

    <ion-header>
      <ion-navbar>
        <ion-title>冒泡ion-title>
      ion-navbar>
    ion-header>
    <ion-content>
      <ion-list>
        <ion-item [navPush]="ChatdetailsPage" [navParams]="userInfo">
          <ion-avatar item-left>
            <img src="">
          ion-avatar>
          <h2>迪丽热巴h2>
          <p>热巴小姐姐Q你了一下哦~p>
        ion-item>
      ion-list>
    ion-content>
    
  • chatbubbles.ts

    import { Component } from '@angular/core';
    import { NavController, NavParams } from 'ionic-angular';
    import {ChatdetailsPage} from '../chatdetails/chatdetails';
    
    @Component({
      selector: 'page-chatbubbles',
      templateUrl: 'chatbubbles.html',
    })
    export class ChatbubblesPage {
    
      userInfo:any;
      ChatdetailsPage:any;
    
      constructor(
        public navCtrl: NavController,
        public navParams: NavParams
        ) {
          //模拟返回聊天用户列表
          this.userInfo = {
            userId:'1',
            userName:'迪丽热巴',
          }
          this.ChatdetailsPage = ChatdetailsPage;
        }
    }
    

二.聊天对话页面布局

1.开发设计

  • 获得传递过来的username并显示在最上方navParams.get('userName')
  • 编写消息聊天布局内容

2.实例代码

  • chatdeatils.html

    <ion-header>
      <ion-navbar>
        <ion-title>{{chatUserName}}ion-title>
      ion-navbar>
    ion-header>
    <ion-content>
      <div class="message-wrap">
        <div class="message right">
          <img src="../../assets/imgs/logo.png" class="user-img"/>
          <div class="msg-detail">
            <div class="msg-info">
              <p>Jack 1分钟前p>
            div>
            <div class="msg-content">
              <p class="line-breaker">自己发的消息内容p>
            div>
          div>
        div>
      div>
    ion-content>
    
  • chatdetails.ts

    import { Component } from '@angular/core';
    import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
    
    @Component({
      selector: 'page-chatdetails',
      templateUrl: 'chatdetails.html',
    })
    export class ChatdetailsPage {
    
      chatUserName:string;
    
      constructor(
        public navCtrl: NavController, 
        public navParams: NavParams,
        public viewCtrl: ViewController) {
          //获得传递过来的username
          this.chatUserName = navParams.get('userName')
      }
    }
    
  • chatdetails.scss

    page-chatdetails {
        $userBackgroundColor: #387ef5;
        $toUserBackgroundColor: #fff;
        ion-content .scroll-content {
          background-color: #f5f5f5;
        }
        ion-footer {
          box-shadow: 0 0 4px rgba(0, 0, 0, 0.11);
          background-color: #fff;
          height: 255px;
        }
        .line-breaker {
          white-space: pre-line;
        }
        .input-wrap {
          padding: 0 5px;
          ion-textarea {
            position: static;
          }
          ion-col.col {
            padding: 0;
          }
          button {
            width: 100%;
            height: 55px;
            font-size: 1.3em;
            margin: 0;
          }
          textarea {
            border-bottom: 1px #387ef5;
            border-style: solid;
          }
        }
        .message-wrap {
          padding: 0 10px;
          .message {
            position: relative;
            padding: 7px 0;
            .user-img {
              position: absolute;
              border-radius: 45px;
              width: 45px;
              height: 45px;
              box-shadow: 0 0 2px rgba(0, 0, 0, 0.36);
            }
            .msg-detail {
              width: 100%;
              display: inline-block;
              p {
                margin: 0;
              }
              .msg-info {
                p {
                  font-size: .8em;
                  color: #888;
                }
              }
              .msg-content {
                position: relative;
                margin-top: 5px;
                border-radius: 5px;
                padding: 8px;
                border: 1px solid #ddd;
                color: #fff;
                width: auto;
                span.triangle {
                  background-color: #fff;
                  border-radius: 2px;
                  height: 8px;
                  width: 8px;
                  top: 12px;
                  display: block;
                  border-style: solid;
                  border-color: #ddd;
                  border-width: 1px;
                  -webkit-transform: rotate(45deg);
                  transform: rotate(45deg);
                  position: absolute;
                }
              }
            }
          }
          .message.left {
            .msg-content {
              background-color: $toUserBackgroundColor;
              float: left;
            }
            .msg-detail {
              padding-left: 60px;
            }
            .user-img {
              left: 0;
            }
            .msg-content {
              color: #343434;
              span.triangle {
                border-top-width: 0;
                border-right-width: 0;
                left: -5px;
              }
            }
          }
          .message.right {
            .msg-detail {
              padding-right: 60px;
              .msg-info {
                text-align: right;
              }
            }
            .user-img {
              right: 0;
            }
            ion-spinner {
              position: absolute;
              right: 10px;
              top: 50px;
            }
            .msg-content {
              background-color: $userBackgroundColor;
              float: right;
              span.triangle {
                background-color: $userBackgroundColor;
                border-bottom-width: 0;
                border-left-width: 0;
                right: -5px;
              }
            }
          }
        }  
    }
    

三.聊天对话页面底部输入框设计

1.开发设计

  • 输入框以及表情选择页面的输入与开发
  • 开发技巧
    • 开发底栏组件使用ion-footer
    • ionic没有边框的属性:no-border
    • 使用ion-grid/ion-row/ion-col实现响应式编程

2.实例代码

  • chatdetails.html

    <ion-header>
      <ion-navbar>
        <ion-title>{{chatUserName}}ion-title>
      ion-navbar>
    ion-header>
    <ion-content>
      <div class="message-wrap">
        <div class="message right">
          <img src="../../assets/imgs/logo.png" class="user-img"/>
          <div class="msg-detail">
            <div class="msg-info">
              <p>Jack 1分钟前p>
            div>
            <div class="msg-content">
              <p class="line-breaker">Hi,热巴小姐姐p>
            div>
          div>
        div>
      div>
    ion-content>
    
    <ion-footer no-border style="height: 55px;">
      <ion-grid class="input-wrap">
        <ion-row>
          <ion-col col-2>
            <button ion-button clear ion-only item-right>
              <ion-icon name="md-happy">ion-icon>
            button>
          ion-col>
          <ion-col col-8>
            <ion-textarea placeholder="请输入内容">ion-textarea>
          ion-col>
          <ion-col col-2>
            <button ion-button clear ion-only item-right>
              <ion-icon name="send">ion-icon>
            button>
          ion-col>
        ion-row>
      ion-grid>
    ion-footer>
    

四.聊天对话页面表情输入模块的开发

1.开发思路

  • 创建providers用于获得emoji表情ionic g provider emoji
  • 生成componentionic g component emojipicker,注意自动创建module,需要添加imports并注册到app.modules中
  • 编写emojipicker组件(Angular表单——实现接口ControlValueAccessor,通过ControlValueAccessor可以将原生dom节点内容转换成表单内容;使用ion-sliders组件可以实现左右滑动切换)和chatdetails页面内容

2.实例代码

  • emoji.ts

    import { HttpClient } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class EmojiProvider {
    
      constructor(public http: HttpClient) {
        console.log('Hello EmojiProvider Provider');
      }
    
      //获取所有表情的数组(已经分组好了的)
      getEmojis() {
        const EMOJIS = "? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?" +
          " ☹️ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?" +
          " ? ? ? ? ? ☠️ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ✊ ? ? ? ✌️ ? ? ? ? ? ? ☝️ ✋ ?" +
          " ? ? ? ? ? ? ✍️ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?‍♀️ ? ? ? ? ?‍♀️ ? ?‍♀️ ? ?‍♀️ ?" +
          " ?‍♀️ ? ?️‍♀️ ?️ ?‍⚕️ ?‍⚕️ ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍? ?‍?" +
          " ?‍? ?‍? ?‍? ?‍? ?‍✈️ ?‍✈️ ?‍? ?‍? ?‍⚖️ ?‍⚖️ ? ? ? ? ? ? ? ? ?‍♀️ ? ? ?‍♂️ ? ?‍♂️ ? ?‍♂️ ? ?‍♂️ ?‍♀️ ?‍♂️ ?‍♀" +
          "️ ?‍♂️ ? ?‍♂️ ? ?‍♂️ ? ?‍♂️ ? ?‍♂️ ? ? ? ? ?‍♂️ ?‍♀️ ? ?‍♀️ ? ? ? ? ? ?‍❤️‍? ?‍❤️‍? ? ?‍❤️‍?‍? ?‍❤️‍?‍? ? ?‍?‍?" +
          " ?‍?‍?‍? ?‍?‍?‍? ?‍?‍?‍? ?‍?‍? ?‍?‍? ?‍?‍?‍? ?‍?‍?‍? ?‍?‍?‍? ?‍?‍? ?‍?‍? ?‍?‍?‍? ?‍?‍?‍? ?‍?‍?‍? ?‍? ?‍?" +
          " ?‍?‍? ?‍?‍? ?‍?‍? ?‍? ?‍? ?‍?‍? ?‍?‍? ?‍?‍? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ⛑ ? ? ? ? ? ?" +
          " ? ? ☂️";
    
        //进行分组的操作
        let array = EMOJIS.split(' ');
        let groupNumber = Math.ceil(array.length / 24); //四舍五入,尽量取大数 15.1->16 , 15.6->16
        let items = [];
    
        //分组填充表情
        for (let i = 0; i < groupNumber; i++) {
          items.push(array.slice(24 * i, 24 * (i + 1)))
        }
        return items;
      }
    }
    
  • emojipicker.ts

    import { Component, forwardRef } from '@angular/core';
    import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
    import { EmojiProvider } from '../../providers/emoji/emoji';
    import { _ParseAST } from '@angular/compiler';
    
    //实现EmojipickerComponent的providers
    export const EMOJI_ACCESSOR:any = {
      provide:NG_VALUE_ACCESSOR,
      useExisting:forwardRef(()=>EmojipickerComponent),
      multi:true
    }
    
    @Component({
      selector: 'emojipicker',
      templateUrl: 'emojipicker.html',
      providers:[EMOJI_ACCESSOR]
    })
    //Angular表单——实现接口ControlValueAccessor
    //可以将dom转成angular form
    export class EmojipickerComponent implements ControlValueAccessor{
     
      //emoji表情
      emojiArray = [];
      //输入信息内容
      content:string;
      onChange:Function;
      onTouched:Function;
    
      constructor(
        emojiProvider:EmojiProvider
      ) {
        this.emojiArray = emojiProvider.getEmojis();//获得所有emoji表情
      }
    
      //写一个值到element上面
      writeValue(obj: any): void {
        this.content = obj;
      }
    
      registerOnChange(fn: any): void {
        this.onChange = fn;
        this.setValue(this.content);
      }
      
      registerOnTouched(fn: any): void {
        this.onTouched = fn;
      }
    
      //在此处新的内容的赋值以及函数的绑定
      setValue(val:any){
        this.content += val;
        if(this.content){
          this.onChange(this.content);
        }
      }
    
    }
    
  • emojipicker.html

    <div class="emoji-picker">
      <div class="emoji-items">
        
        <ion-slides pager>
          <ion-slide *ngFor="let items of emojiArray">
            <span class="emoji-item" (click)="setValue(item)" *ngFor="let item of items">
              {{item}}
            span>
          ion-slide>
        ion-slides>
      div>
    div>
    
  • emojipicker.scss

    emojipicker {
        .emoji-picker{
            height: 195px;
            border-top:1px solid #999;
    
            .emoji-items{
                padding: 10px;
                width: 100%;
                height: 100%;
    
                .emoji-item{
                    display: block;
                    float: left;
                    width: 12.5%;
                    height: 42px;
                    font-size: 1.2em;
                    line-height: 42px;
                    text-align: center;
                    margin-bottom: 10px;
                }
            }
        }
    }
    
  • component.module.ts

    import { NgModule } from '@angular/core';
    import { EmojipickerComponent } from './emojipicker/emojipicker';
    import { IonicPageModule } from 'ionic-angular';
    @NgModule({
    	declarations: [EmojipickerComponent],
    	imports: [IonicPageModule.forChild(EmojipickerComponent)],
    	exports: [EmojipickerComponent]
    })
    export class ComponentsModule {}
    
  • chatdetails.ts

    import { Component } from '@angular/core';
    import { IonicPage, NavController, NavParams, ViewController } from 'ionic-angular';
    
    @Component({
      selector: 'page-chatdetails',
      templateUrl: 'chatdetails.html',
    })
    export class ChatdetailsPage {
    
      chatUserName:string;
      //决定emoji是否显示
      isOpenEmojiPicker = false;
    
      constructor(
        public navCtrl: NavController, 
        public navParams: NavParams,
        public viewCtrl: ViewController) {
          //获得传递过来的username
          this.chatUserName = navParams.get('userName')
      }
    
      //切换emoji是否显示
      swichEmojiPicker() {
        this.isOpenEmojiPicker = !this.isOpenEmojiPicker;
      }
    }
    
  • chatdetails.html

    <ion-header>
      <ion-navbar>
        <ion-title>{{chatUserName}}ion-title>
      ion-navbar>
    ion-header>
    <ion-content>
      <div class="message-wrap">
        <div class="message right">
          <img src="../../assets/imgs/logo.png" class="user-img"/>
          <div class="msg-detail">
            <div class="msg-info">
              <p>Jack 1分钟前p>
            div>
            <div class="msg-content">
              <p class="line-breaker">Hi,热巴小姐姐p>
            div>
          div>
        div>
      div>
    ion-content>
    
    <ion-footer no-border [style.height]="isOpenEmojiPicker?'255px':'55px'">
      <ion-grid class="input-wrap">
        <ion-row>
          <ion-col col-2>
            <button ion-button clear ion-only item-right (click)="swichEmojiPicker()">
              <ion-icon name="md-happy">ion-icon>
            button>
          ion-col>
          <ion-col col-8>
            <ion-textarea placeholder="请输入内容">ion-textarea>
          ion-col>
          <ion-col col-2>
            <button ion-button clear ion-only item-right>
              <ion-icon name="send">ion-icon>
            button>
          ion-col>
        ion-row>
      ion-grid>
      
      <emojipicker *ngIf="isOpenEmojiPicker">emojipicker>
    ion-footer>
    
  • chatdetails.scss

    page-chatdetails {
        $userBackgroundColor: #387ef5;
        $toUserBackgroundColor: #fff;
        ion-content .scroll-content {
          background-color: #f5f5f5;
        }
        ion-footer {
          box-shadow: 0 0 4px rgba(0, 0, 0, 0.11);
          background-color: #fff;
          height: 255px;
        }
        .line-breaker {
          white-space: pre-line;
        }
        .input-wrap {
          padding: 0 5px;
          ion-textarea {
            position: static;
          }
          ion-col.col {
            padding: 0;
          }
          button {
            width: 100%;
            height: 55px;
            font-size: 1.3em;
            margin: 0;
          }
          textarea {
            border-bottom: 1px #387ef5;
            border-style: solid;
          }
        }
        .message-wrap {
          padding: 0 10px;
          .message {
            position: relative;
            padding: 7px 0;
            .user-img {
              position: absolute;
              border-radius: 45px;
              width: 45px;
              height: 45px;
              box-shadow: 0 0 2px rgba(0, 0, 0, 0.36);
            }
            .msg-detail {
              width: 100%;
              display: inline-block;
              p {
                margin: 0;
              }
              .msg-info {
                p {
                  font-size: .8em;
                  color: #888;
                }
              }
              .msg-content {
                position: relative;
                margin-top: 5px;
                border-radius: 5px;
                padding: 8px;
                border: 1px solid #ddd;
                color: #fff;
                width: auto;
                span.triangle {
                  background-color: #fff;
                  border-radius: 2px;
                  height: 8px;
                  width: 8px;
                  top: 12px;
                  display: block;
                  border-style: solid;
                  border-color: #ddd;
                  border-width: 1px;
                  -webkit-transform: rotate(45deg);
                  transform: rotate(45deg);
                  position: absolute;
                }
              }
            }
          }
          .message.left {
            .msg-content {
              background-color: $toUserBackgroundColor;
              float: left;
            }
            .msg-detail {
              padding-left: 60px;
            }
            .user-img {
              left: 0;
            }
            .msg-content {
              color: #343434;
              span.triangle {
                border-top-width: 0;
                border-right-width: 0;
                left: -5px;
              }
            }
          }
          .message.right {
            .msg-detail {
              padding-right: 60px;
              .msg-info {
                text-align: right;
              }
            }
            .user-img {
              right: 0;
            }
            ion-spinner {
              position: absolute;
              right: 10px;
              top: 50px;
            }
            .msg-content {
              background-color: $userBackgroundColor;
              float: right;
              span.triangle {
                background-color: $userBackgroundColor;
                border-bottom-width: 0;
                border-left-width: 0;
                right: -5px;
              }
            }
          }
        }  
    }
    

五.聊天对话页面自动回复逻辑开发

1.开发设计

  • 实现进入冒泡页面查看详情,通过mock的json数据查看历史消息,创建emojipicker组件实现发送表情,通过消息订阅实现消息返回并被用户监听查看

  • 使用mock的json模拟后台的返回数据

    • 如何在开发中mock数据

      • 创建mock的json文件

      • 请求返回数据的内容与发送请求访问服务一样

        getMessageList():Promise{
            const url = '../../assets/mock/msg-list.json';
            return this.http.get(url)
              .toPromise()
              .then((response:any) => response.array as ChatMessage[])
              .catch(error => Promise.reject(error || '错误信息'));
        }
        
  • 使用ViewChild获取页面DOM节点

    • 使用Content可以获取整个页面内容,调用this.content.scrollToBottom();可以将页面滚到底部
    • 使用具体值可以定位具体DOM节点,在html中通过#名称标记DOM,在ts中通过@ViewChild('名称') messageInput:TextInput;获取
  • Events模块实现消息订阅[订阅者模式]

    • 订阅消息使用:subscribe方法

      //参数:订阅topic、回调函数(消息内容,时间)
      this.events.subscribe('chat.received',(msg,time)=>{
          this.messageList.push(msg);
          this.scrollToBottom();
      })
      
    • 发布消息使用:publish方法

      //将消息发布出去,参数: 事件topic,消息内容,时间
      this.events.publish("chat.received",messageSend,Date.now());
      
    • 取消订阅使用:unsubscribe方法

      //取消订阅,参数: 事件topic
      this.events.unsubscribe('chat.received');
      
  • 安装moment:npm install moment --save,moment是npm轻量级的js数据库,引入moment的方式import * as moment from 'moment';[所有引入javascript包都是如此]

  • 管道(像管子一样将你定义的方法“流到”所有你需要的地方去),创建管道ionic g pipe relativtime

2.实例代码

  • 处理时间,获得与当前时间差情况的pipe文件,relativetime.ts

    import { Pipe, PipeTransform } from '@angular/core';
    import * as moment from 'moment';
    @Pipe({
      name: 'relativetime',
    })
    export class RelativetimePipe implements PipeTransform {
      transform(value: string, ...args) {
        //使用moment(时间戳).toNow()则可以得到时间戳距现在多久了
        return moment(value).toNow();
      }
    }
    
  • 聊天消息处理,用于查看、发送、模拟消息处理与订阅发布的service,chatservice.ts

    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Events } from 'ionic-angular';
    
    //聊天信息的属性
    export class ChatMessage {
      messageId:string;
      userId:string;
      userName:string;
      userImgUrl:string;
      toUserId:string;//发送给谁的Id
      time:number | string;
      message:string;
      status:string;
    }
    
    //用户信息的属性
    export class UserInfo {
      userId:string;
      userName:string;
      userImgUrl:string;
    }
    
    @Injectable()
    export class ChatserviceProvider {
    
      constructor(
        public http: HttpClient,
        public events: Events
        ) {}
    
      /**
       * 获取消息列表
       * 从获取的JSON中获取
       */
      getMessageList():Promise{
        const url = '../../assets/mock/msg-list.json';
        return this.http.get(url)
          .toPromise()
          .then((response:any) => response.array as ChatMessage[])
          .catch(error => Promise.reject(error || '错误信息'));
      }
    
      /**
       * 发送消息
       * @param message 
       */
      sendMessage(message:ChatMessage) {
        return new Promise(resolve => setTimeout(()=>{
          resolve(message)
        },Math.random()*1000))
        .then(()=>{
          //模拟返回消息
          this.mockNewMessage(message);
        });
      }
    
      /**
       * 模拟对方返回消息
       * 使用发布订阅模式实现即时接受消息
       * 引入Events模块
       * Events is a public-subscribe style event system for sending and responding to application-level event across your app
       * 
       * @param message 
       */
      mockNewMessage(message:ChatMessage) {
        const id = Date.now().toString();
        let messageSend:ChatMessage = {
          messageId : id,
          userId : '123321',
          userName : '迪丽热巴',
          userImgUrl : '../../assets/imgs/girl.jpg',
          toUserId : message.userId,
          time : Date.now(),
          message : '不要跟我说:'+message.message+'我只想给你一个么么哒!! ?',
          status : 'seccess'
        }
        //模拟网络请求卡一下
        setTimeout(()=>{
          //将消息发布出去,参数: 事件主题,消息内容,时间
          this.events.publish("chat.received",messageSend,Date.now());
        }, Math.random()*1000);
      }
    }
    
  • 聊天详情页面布局,chatdetails.html

    <ion-header>
      <ion-navbar>
        <ion-title>{{chatUserName}}ion-title>
      ion-navbar>
    ion-header>
    <ion-content>
      <div class="message-wrap">
        <div class="message" 
          *ngFor="let m of messageList"
          [class.left]="m.userId === chatUserId"
          [class.right]="m.userId === userId">
          <img [src]="m.userImgUrl" class="user-img"/>
          
          <ion-spinner name="dots" *ngIf="m.status === 'pending'">ion-spinner>
          <div class="msg-detail">
            <div class="msg-info">
              <p>{{m.userName}} {{m.time | relativetime}}p>
            div>
            <div class="msg-content">
              <p class="line-breaker">{{m.message}}p>
            div>
          div>
        div>
    
      div>
    ion-content>
    
    <ion-footer no-border [style.height]="isOpenEmojiPicker?'255px':'55px'">
      <ion-grid class="input-wrap">
        <ion-row>
          <ion-col col-2>
            <button ion-button clear ion-only item-right (click)="swichEmojiPicker()">
              <ion-icon name="md-happy">ion-icon>
            button>
          ion-col>
          <ion-col col-8>
            <ion-textarea 
              #chatInput
              [(ngModel)]="editorMessage"
              (keyup.enter)="sendMessage()"
              (focus)="focus()"
              placeholder="请输入内容">ion-textarea>
          ion-col>
          <ion-col col-2>
            <button ion-button clear ion-only item-right (click)="sendMessage()">
              <ion-icon name="send">ion-icon>
            button>
          ion-col>
        ion-row>
      ion-grid>
      <emojipicker *ngIf="isOpenEmojiPicker" [(ngModel)]="editorMessage">emojipicker>
    ion-footer>
    
  • 聊天详情页面控制器,chatdetails.ts

    import { Component, ViewChild } from '@angular/core';
    import { NavController, NavParams, ViewController, Content, TextInput, Events } from 'ionic-angular';
    import { ChatserviceProvider, ChatMessage } from '../../providers/chatservice/chatservice';
    import { Storage } from '@ionic/storage';
    @Component({
      selector: 'page-chatdetails',
      templateUrl: 'chatdetails.html',
    })
    export class ChatdetailsPage {
    
      //和哪个username聊天
      chatUserName:string;
      //和哪个userid聊天
      chatUserId:string;
      //当前用户信息
      userId:string;
      userName:string;
      userImgUrl:string;
    
      //决定emoji是否显示
      isOpenEmojiPicker = false;
      //对话消息列表
      messageList:ChatMessage[] = [];
    
      //获取页面DOM节点
      //获取整个页面是Content
      @ViewChild(Content) content:Content;
      //用户获取用户输入的输入框
      @ViewChild('chatInput') messageInput:TextInput;
      editorMessage:string='';
    
      constructor(
        public navCtrl: NavController, 
        public navParams: NavParams,
        public viewCtrl: ViewController,
        public chatService: ChatserviceProvider,
        public storage: Storage,
        public events: Events) {
          //获得传递过来的username
          this.chatUserName = navParams.get('userName');
          //获取当前和谁聊天的userId
          this.chatUserId = navParams.get('userId');
      }
    
      //页面进来时发送的请求ionViewDidEnter[注意不能使用ionViewDidLoad,如果使用页面会闪一下,ionViewDidLoad是页面渲染后完成的内容]
      ionViewDidEnter() {
        this.storage.get('token').then((val)=>{
          if(val !== null){
            this.userId = '140000198202211138';
            this.userName = 'Jack汪喆';
            this.userImgUrl = '../../assets/imgs/avatar.jpg';
          }
        })
        this.getMessage()
          .then(()=>{
            this.scrollToBottom();//消息显示完成后需要将页面滚到最下面
        })
        //听取消息的发布和订阅
        //此处通过消息主题实现订阅
        this.events.subscribe('chat.received',(msg,time)=>{
          this.messageList.push(msg);
          this.scrollToBottom();
        })
      }
    
      //在将要离开页面时,退订订阅的内容
      ionViewWillLeave() {
        this.events.unsubscribe('chat.received');
      }
    
      //切换emoji是否显示
      swichEmojiPicker() {
        this.isOpenEmojiPicker = !this.isOpenEmojiPicker;
      }
    
      //获得消息内容,调用service中属性的方法实现属性的赋值
      getMessage() {
        //返回的是promise
        return this.chatService.getMessageList()
          .then(res=>{
            this.messageList = res;
          })
          .catch((error)=>{
            console.log(error);
          });
      }
    
      //页面滚动到最下面
      scrollToBottom() {
        setTimeout(()=>{
          if(this.content.scrollToBottom){
            this.content.scrollToBottom();
          }
        },400);
      }
    
      //发送消息
      sendMessage() {
        //如果为空则不做任何处理,即不发送消息
        if(!this.editorMessage.trim()){
          return;
        }
        const id = Date.now().toString();
        let messageSend:ChatMessage = {
          messageId : id,
          userId : this.userId,
          userName : this.userName,
          userImgUrl : this.userImgUrl,
          toUserId : this.chatUserId,
          time : Date.now(),
          message : this.editorMessage,
          status : 'pending'//表示消息的状态,pending表示正在发送中
        }
        this.messageList.push(messageSend);
        this.scrollToBottom();
        this.editorMessage = '';
        if(!this.isOpenEmojiPicker){
          //如果表情窗口没被打开,则将输入信息窗口聚焦
          this.messageInput.setFocus();
        }
        this.chatService.sendMessage(messageSend)
          .then(()=>{
            let index = this.getMessageIndex(id);
            if(index!==-1){
              this.messageList[index].status = 'success';
            }
          });
      }
    
      //聚焦事件:当光标移到textarea时触发
      focus() {
        this.isOpenEmojiPicker = false;
        this.content.resize();//重新计算整个页面DOM节点的内容
        this.scrollToBottom();
      }
    
      //从当前消息列表中获取对应messageId的下标的message
      getMessageIndex(id:string){
        return this.messageList.findIndex(e=>{
          return e.messageId === id;
        })
      }
    }
    

你可能感兴趣的:(Ionic)