鸿蒙云信聊天界面的实现

鸿蒙有上拉加载更多和下拉刷新的框架,可聊天记录是要求下拉加载更多,没办法,只好自己实现了。
不废话,直接上代码
page界面

import { V2NIMMessage, V2NIMMessageType } from '@nimsdk/base';
import { Route, ZRouter } from '@taole/basecommon';
import { V2NIMMessageDataSource } from '../model/V2NIMMessageDataSource';
import { common } from '@kit.AbilityKit';
import { onReceiveMessagesKey, onSendMessageKey } from '../utils/ConstantUtil';
import { emitter } from '@kit.BasicServicesKit';
import { V2NIMUserDataSource } from '../model/V2NIMUserDataSource';
import { ChatPageModel } from '../viewModel/ChatPageModel';
import { dateFormat } from '../utils/IMUtil';
import { ParamsToChatPage } from '../components/ImTabContent';

@Route({ name: 'ChatPageDestination', needLogin: true })
@Component
@Preview
export struct ChatPage {
  mContext = getContext(this) as common.UIAbilityContext;
  @State @Watch("historyMessageListUpdate") //每次获取的消息列表,
  historyMessageList: V2NIMMessage[] = [];
  historyMessageDataSource: V2NIMMessageDataSource = new V2NIMMessageDataSource();
  v2NIMUserDataSource: V2NIMUserDataSource = new V2NIMUserDataSource();
  mScroller: Scroller = new ListScroller();
  @State
  viewModel: ChatPageModel = new ChatPageModel();
  //第一次滑动到顶部,数据加载默认在顶部,第一次不需要处理,所以初始值为false
  isLoadingMore: boolean = false;
  isInti: boolean = false;
  @State @Watch("isNearTopUpdate")
  isNearTop:boolean = true;

  isNearTopUpdate(propertyName: string){
    if(this.isNearTop){
      console.error("张飞,加载更多")
      this.loadMore();
    }
  }
  async historyMessageListUpdate(propertyName: string) {
    if (!this.isLoadingMore) {
      let sendUsers = await this.viewModel.getSenderUser(this.historyMessageList);
      this.historyMessageDataSource.pushAllData(this.historyMessageList);
      this.v2NIMUserDataSource.pushAllData(sendUsers);
      console.error("张飞,添加数据 -- 收发消息", `数据个数${this.historyMessageList.length}     ` +
      dateFormat(this.historyMessageDataSource.getTheLatestData().createTime));
      //滚动到最新的消息
      this.scrollToTheLatest();
    } else {
      //加载更多要在第0处添加
      let sendUsers = await this.viewModel.getSenderUser(this.historyMessageList);
      this.historyMessageDataSource.addAllData(0, this.historyMessageList);
      this.v2NIMUserDataSource.addAllData(0, sendUsers);
      console.error("张飞,添加数据 -- 加载更多", `数据个数${this.historyMessageList.length}     ` +
      dateFormat(this.historyMessageDataSource.getData(0).createTime));
      this.mScroller.scrollToIndex(this.viewModel.limit)
      this.isLoadingMore = false;
      // this.historyMessageList.unshift();
      // sendUsers.unshift()
    }
    this.viewModel.messageNumber = this.historyMessageDataSource.totalCount();
  }

  async aboutToAppear(): Promise<void> {
    console.log("张飞", "---------------------------aboutToAppear ChatPage")
    //获取上个界面传递过来的conversationID
    let params = ZRouter.getParam() as ParamsToChatPage;
    this.viewModel.conversationID = params.conversationIdKey;
    this.viewModel.conversationUpdateTime = params.updateTime;
    this.viewModel.conversationCreateTime = params.createTime;
    //监听消息的发送和接收
    emitter.on(this.viewModel.receiveInnerEvent(), async (eventData: emitter.EventData): Promise<void> => {
      console.error("张飞:接收的数据添加开始");
      let receiveMessage = eventData.data?.onReceiveMessagesKey as V2NIMMessage[];
      this.historyMessageList = receiveMessage;
    })
    emitter.on(this.viewModel.sendInnerEvent(), async (eventData: emitter.EventData): Promise<void> => {
      console.error("张飞:发送的数据添加开始");
      let sendMessage = eventData.data?.onSendMessageKey as V2NIMMessage
      this.historyMessageList = [sendMessage];
    })
    //获取历史会话记录
    let historyChatTemp = await this.viewModel.getHistoryMessageList()
    console.log("张飞", "查询到的数据条数:" + historyChatTemp.length)
    this.historyMessageList = historyChatTemp;
  }

  build() {
    NavDestination() {
      Column() {
        this.chatRecord();
        this.inputContent();
        this.bottomButton();
      }
    }.width('100%')
    .height('100%')
    .backgroundColor($r('app.color.colorF6F7F9'))
    .hideTitleBar(true)

  }

  //聊天记录
  @Builder
  chatRecord() {
    if (this.viewModel.messageNumber == 0) {
      Stack({ alignContent: Alignment.Center }) {
        Text("我是空白页面"+this.historyMessageList.length);
      }.width('100%').layoutWeight(1)
    } else {
      List({ space: 15, scroller: this.mScroller, }) {
        LazyForEach(this.historyMessageDataSource,
          (item: V2NIMMessage, index: number) => {
            ListItem() {
              this.chatRecordItem(item, index);
            }
          }, (item: V2NIMMessage) => JSON.stringify(item))
      }
      .width('100%')
      .scrollBar(BarState.Off)
      .onScrollIndex((start:number,end:number,center:number)=>{
          this.isNearTop = start < 4;
      })
      .onReachStart(() => {
        if (this.isInti) {
          this.isInti = true;
        }
      })
      .layoutWeight(1)
    }

  }

  //加载更多的方法
  async loadMore() {
    console.error("张飞:加载更多")
    this.isLoadingMore = true;
    let historyChatTemp = await this.viewModel.getHistoryMessageList(this.historyMessageDataSource.getData(0))
    this.historyMessageList = historyChatTemp;
  }

  //聊天记录的chat
  @Builder
  chatRecordItem(item: V2NIMMessage, index: number) {
    if (item.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TIPS) {
      Row() {
        Blank()
        Text(item.text)
          .fontSize('15sp')
          .textAlign(TextAlign.Center)
          .fontColor($r('app.color.color999999'))
          .backgroundColor(Color.White)
          .borderRadius('15vp')
          .padding({
            left: 12,
            top: 5,
            right: '12vp',
            bottom: '5vp'
          })
        Blank()
      }.width('100%')
    } else {
      if (item.isSelf) {
        //自己发送的消息
        Row() {
          Blank()
          Text(dateFormat(item.createTime)).fontColor(Color.Blue)
          Text(item.text).fontColor($r('app.color.color333333')).fontSize('15sp')
            .margin({ right: 6 })
          Image(this.getSenderAvatar(index)).width('42vp').height('42vp')
            .margin({ right: 15 })
            .borderRadius('21vp')
        }.width('100%')
      } else {
        //别人发送的消息
        Row() {
          Image(this.getSenderAvatar(index))
            .margin({ left: '15vp' })
            .width('42vp')
            .height('42vp')
            .borderRadius('21vp')
          Text(item.text).fontColor($r('app.color.color333333')).fontSize('15sp')
            .margin({ left: 6 })
          Text(dateFormat(item.createTime)).fontColor(Color.Blue)
        }.width('100%')
      }
    }
  }

  getSenderAvatar(index: number): ResourceStr {
    let v2NIMUser = this.v2NIMUserDataSource.getData(index);
    if (v2NIMUser != undefined && v2NIMUser.avatar != undefined) {
      return v2NIMUser.avatar;
    } else {
      return $r('app.media.uc_empty_pic');
    }
  }

  //输入内容
  @Builder
  inputContent() {
    Row() {
      Stack({ alignContent: Alignment.End }) {
        TextArea({ text: this.viewModel.sendInputValue, placeholder: $r('app.string.inputMessagePlaceHolder') })
          .backgroundColor(Color.White).width('100%').height('100%')
          .onChange((value: string) => {
            this.viewModel.sendInputValue = value;
          })
        Image($r('app.media.face')).width('30vp').height('30vp').margin({ right: '15vp' })
      }
      .layoutWeight(1)
      .height('100%')
      .margin({ left: '15vp', right: '10vp' })


      Text($r('app.string.send'))
        .margin({ right: '15vp' })
        .fontColor($r('app.color.color666666'))
        .fontSize("15sp")
        .width('65vp')
        .height('100%')
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.White)
        .borderRadius('18vp')
        .onClick(() => {
          if (this.viewModel.sendInputValue.length != 0) {
            console.error("张飞QWERTY", "点击了发送按钮")
            this.viewModel.sendMessage(this.viewModel.createTextMessage());
            this.viewModel.sendInputValue = '';
          }
        })
    }.width('100%').borderRadius("18vp").height('36vp')
  }

  //底部的语音、图片、拍照、礼物
  @Builder
  bottomButton() {
    Row() {
      Image($r('app.media.voice')).bottomButton()
      Image($r('app.media.picture')).bottomButton()
      Image($r('app.media.camera')).bottomButton()
      Image($r('app.media.gift')).bottomButton()
    }.justifyContent(FlexAlign.SpaceEvenly)
    .alignItems(VerticalAlign.Center)
    .margin({ top: '5vp' })
    // .height('44vp')
    .width('100%')
  }

  scrollToTheLatest() {
    setTimeout(() => {
      if (this.historyMessageDataSource.totalCount() > 0) {
        this.mScroller.scrollToIndex(this.historyMessageList.length - 1, false)
      }
    }, 100)
  }
}

@Extend(Image)
function bottomButton() {
  .width('34vp')
  .height('34vp')
}

viewmodel类

import { V2NIMMessage, V2NIMUser } from '@nimsdk/base';
import { imUtil } from '../../../../Index';
import { emitter } from '@kit.BasicServicesKit';
import { onReceiveMessagesEventID, onSendMessageKeyEventID } from '../utils/ConstantUtil';
import { currentData, dataBeforeNow, dateFormat } from '../utils/IMUtil';
import { Prompt } from '@kit.ArkUI';

@Observed
export class ChatPageModel {
  //上个界面传递过来的conversationID
  conversationID: string = '';
  //上个界面传递过来的最后更新时间
  conversationUpdateTime: number = 0;
  //会话创建时间
  conversationCreateTime: number = 0;
  //发送框输入的内容
  sendInputValue: string = ''
  limit: number = 22;
  messageNumber : number = 0;


  async getHistoryMessageList(endMessage?: V2NIMMessage): Promise<V2NIMMessage[]> {
    let endTimeTemp : number= 0;
    if(endMessage==undefined){
      endTimeTemp = this.conversationUpdateTime;
    }else{
      endTimeTemp = endMessage.createTime;
    }
    //如果查询的结束时间早于会话的创建时间,那就没必要查了
    if (endTimeTemp < this.conversationCreateTime) {
      Prompt.showToast({ message: "没有更早的聊天记录了", duration: 2000 });
      return [];
    }
    let messageList =  await imUtil.getHistoryMessageList(this.conversationID,
      this.conversationCreateTime, endMessage==undefined?this.conversationUpdateTime:0, this.limit,endMessage)
    console.error("张飞历史消息列表", `查询到的数量:${messageList.length}  第新时间:${dateFormat(messageList[0].createTime)}   最老时间:${dateFormat(messageList[messageList.length-1].createTime)}`);
    messageList.reverse();
    return messageList;
  }

  async getSenderUser(messageList: V2NIMMessage[]): Promise<V2NIMUser[]> {
    let userID: string[] = [];
    messageList.forEach((value, index) => {
      userID.push(value.senderId)
    });
    return await imUtil.getUserById(userID)
  }

  //创建文本消息
  createTextMessage(): V2NIMMessage {
    return imUtil.createTextMessage(this.sendInputValue)
  }

  //发送消息
  sendMessage(message: V2NIMMessage) {
    console.error("张飞", "发送消息啦" + message.text)
    try {
      imUtil.sendMessage(message, this.conversationID)
    } catch (e) {
      throw new Error("张飞 发送消息错误:" + JSON.stringify(e));
    }

  }

  receiveInnerEvent(): emitter.InnerEvent {
    let event: emitter.InnerEvent = {
      eventId: onReceiveMessagesEventID
    }
    return event;
  }

  sendInnerEvent(): emitter.InnerEvent {
    let event: emitter.InnerEvent = {
      eventId: onSendMessageKeyEventID
    }
    return event;
  }

}





其它相关的类

//对外暴露,供主项目调
import { common } from '@kit.AbilityKit';
import BuildProfile from 'BuildProfile';
import { HttpConfigType, httpHelper, HttpResponse, HttpTask, RequestMethod, } from '@taole/basecommon'
import baseUrl from '../apiService/BaseUrl';
import { loginImApi } from '../apiService/ApiServices';
import { HttpTaskBean } from '@taole/basecommon/src/main/ets/commnet/model/HttpTaskBean';
import { LoginIMBean } from '../model/LoginIMBean';
import {
  LoginIMBeanKey,
} from './ConstantUtil';
import { promptAction } from '@kit.ArkUI';
import {
  V2NIMConversation,
  V2NIMConversationResult,
  V2NIMError,
  V2NIMLoginOption,
  V2NIMMessage,
  V2NIMMessageListOption,
  V2NIMMessageSendingState,
  V2NIMQueryDirection,
  V2NIMUser
} from '@nimsdk/base';
import nimRepository from './NimRepository';
import { BusinessError, emitter } from '@kit.BasicServicesKit';
import { dateFormat } from './IMUtil';

/**
 * 相关文档  https://doc.yunxin.163.com/messaging2/guide/zExMDk4ODU?platform=client#%E7%9B%91%E5%90%AC%E4%BC%9A%E8%AF%9D%E7%9B%B8%E5%85%B3%E4%BA%8B%E4%BB%B6
 *          https://doc.yunxin.163.com/messaging2/client-apis/DAxNjk0Mzc?platform=client#V2NIMConversationType
 *          https://doc.yunxin.163.com/messaging2/client-apis/zA4NjQzOTQ?platform=client#getConversationList
 */
class NIMUtil {
  /**
   * 获取用户信息
   * @param mContext
   */
  getIMUserInfo(mContext: common.Context) {
    const url = baseUrl.getBase_URL_U(BuildProfile.BUILD_MODE_NAME) + loginImApi;
    let httpTaskBean = new HttpTaskBean()
    httpTaskBean.axiosConfigType = HttpConfigType.IM
    httpHelper.initHttp(httpTaskBean)
      .setUrl(url)
      .setRequestMethod(RequestMethod.GET)
      .setResponse(new HttpResponse<LoginIMBean>((result: LoginIMBean) => {
        if (result.error == 0) {
          //存储LoginIMBean
          AppStorage.setOrCreate(LoginIMBeanKey, result);
          console.error("张飞", "注册服务1");
          console.error("张飞", "appKey:" + result.appkey)
          console.error("张飞", "accid:" + result.accid)
          console.error("张飞", "token:" + result.token)
          //result = new LoginIMBean(1,20720676,'12a8a3e936bb6b6764108680e1daa82c','712eb79f6472f09e9d8f19aecba1cf43')
          //登录IM
          this.loginIM(mContext, result)
        } else {
          promptAction.showToast({ message: `错误信息${result.error}`, duration: 2000 })
        }
      }))
      .run()
  }

  /**
   * 获取用户信息后登录
   * @param mContext
   * @param result
   */
  private loginIM(mContext: common.Context, result: LoginIMBean) {
    //初始化NimRepository
    nimRepository.init(mContext, result.appkey);

    let loginOption = {
      retryCount: 3,
      forceMode: false,
      authType: 0,
      timeout: 60000,
      syncLevel: 0,
    } as V2NIMLoginOption
    try {
      nimRepository.nim.loginService.login(result.accid.toString(), result.token, loginOption)
      console.error("张飞", "IM登录成功");
    } catch (error) {
      this.processV2ErrorForPrint(error);
    }
  }

  processV2ErrorForPrint(err: V2NIMError): string {
    if (err instanceof Error) {
      let desc = `\n${err.name}\n code: ${err.code}\n message: "${err.message}"\n detail: ${err.detail ?
      JSON.stringify(err.detail) : ''}`
      if (err?.detail?.rawError) {
        desc += `\n rawError: ${err.detail.rawError.message}`
        console.log('张飞----------- 登录失败222 -----------', desc)
      }
      return desc
    } else {
      return `Caught an exception: ${err}`
    }
  }

  /**
   * 获取对话列表
   * @returns
   */
  async getConversationList(): Promise<V2NIMConversation[]> {
    console.info(`张飞----------查询会话列表--------- \n`)
    try {
      // get service
      let conversationService = nimRepository.nim.conversationService! // Demo启用conversation模块,conversationService不为空
      conversationService.getConversationList(0, 12)
      // call interface
      const res = await conversationService.getConversationList(0, 12)
      const resString = JSON.stringify(res);
      let conversationListResponseBean: V2NIMConversationResult = JSON.parse(resString);
      console.info(`张飞-----------查询会话列表成功00----------- ${res}`)
      console.info('张飞bean类', JSON.stringify(conversationListResponseBean));
      return conversationListResponseBean.conversationList
      // emitter.emit("getConversationList",)
    } catch (err) {
      console.error(`张飞-----------查询会话列表失败---------- \n`, `${err}`)
      return [];
    }
  }

  //获取历史消息列表
  async getHistoryMessageList(conversationId: string,beginTime:number,endTime:number,mLimit:number,anchorMessage?:V2NIMMessage): Promise<V2NIMMessage[]> {
    let messageService = nimRepository.nim.messageService!
    const option: V2NIMMessageListOption = {
      conversationId: conversationId,
      //查询的开始时间
      beginTime:beginTime,
      //查询的结束时间
      endTime:endTime,
      //查询的条数
      limit: mLimit,
      //查询的消息锚点,如果不为空,以该字段为准
      anchorMessage:anchorMessage,
      //按时间戳查询,V2NIM_QUERY_DIRECTION_DESC降序(最新的消息在上面),V2NIM_QUERY_DIRECTION_ASC升序(最新的消息在下面)
      direction: V2NIMQueryDirection.V2NIM_QUERY_DIRECTION_DESC,
      //是否按照反方向排序
      reverse: false
    }
    if(anchorMessage!=undefined) {
      console.error("张飞查询前的参数打印1 定义了锚点信息", dateFormat(anchorMessage?.createTime)+"  结束时间",dateFormat(endTime));
    }else{
      console.error("张飞查询前的参数打印0 未定义锚点信息", "开始时间:"+dateFormat(beginTime)+"  结束时间",dateFormat(endTime));
    }
    try {
      const messageList: V2NIMMessage[] = await messageService.getMessageList(option);
      return messageList;
    }catch(e){
      console.error(`张飞:历史消息列表查询错误   ${e}`);
      throw new Error(`张飞:历史消息列表查询错误   ${JSON.stringify(e)}`)
    }


  }

  //根据用户id获取用户
  async getUserById(accountIDs: string[]): Promise<V2NIMUser[]> {
    let userList = await nimRepository.nim.userService?.getUserList(accountIDs);
    if (userList == undefined) {
      return [];
    } else {
      return userList;
    }
  }

  //创建文本消息
  createTextMessage(text: string): V2NIMMessage {
    const message = nimRepository.nim.messageCreator.createTextMessage(text)
    return message;
  }

  sendMessage(message: V2NIMMessage, conversationId: string) {
    nimRepository.nim.messageService?.sendMessage(message, conversationId);
  }
}

export default new NIMUtil();



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值