HarmonyOS中封装聊天页面

使用WebSocket建立服务器与客户端的双向连接

WebSock

import http from '@ohos.net.htt
import util from '@ohos.util'
import promptAction from '@ohos.promptAction'
import { webSocket } from '@kit.NetworkKit'
import { BusinessError } from '@kit.BasicServicesKit'

@Entry
@Component
struct ChatMessage {
  @State
  contentStr: string = ""
  @State
  messList: MessageItem[] = []
  scroller: Scroller = new Scroller()
  @State
  sendIng: boolean = false
  token: string = ""
  ws: webSocket.WebSocket = webSocket.createWebSocket()

  aboutToAppear() {
    this.getToken()
  }
  aboutToDisappear(): void {
    this.ws.close()
  }

  async getToken() {
    const instance = http.createHttp()
    const res = await instance.request('https://toutiao.itheima.net/v1_0/authorizations', {
      header: {
        "Content-Type": "application/json",
      },
      method: http.RequestMethod.POST,
      extraData: { mobile: '13912345678', code: '246810' }
    })
    const tokenObj = JSON.parse(res.result as string) as ResponseResult<Token>
    this.token = tokenObj.data.token
    this.createConnect()
  }

  // 创建连接
  async createConnect() {
    if (this.token) {
      try {
        // 1.连接
        this.ws.connect('ws://toutiao.itheima.net/socket.io/?EIO=4&transport=websocket&token=' + this.token,
          (err: BusinessError, value: boolean) => {
            if (!err) {
              console.log("hmsocket connect success");
            } else {
              console.log("hmsocket connect fail, err:" + JSON.stringify(err));
            }
          })
        // 2.收消息
        this.ws.on('message', (err: BusinessError<void>, value: string | ArrayBuffer) => {
          console.log("hmsocket on message, message:" + value);
          // 协议1:收到0开头回复40
          if ((value as string).startsWith('0')) {
            this.ws.send('40')
          } else if ((value as string).startsWith('2')) {
            // 协议2:收到2回复3
            this.ws.send('3')
          } else if ((value as string).startsWith('42')) {
            // 协议3:收到42开头,type为'message'是消息,current是心跳
            console.log('hmsocket message value slice:' + (value as string).replace('42', ''))
            if (JSON.parse((value as string).replace('42', ''))[0] === 'message') {
              // promptAction.showToast({
              //   message: JSON.parse((value as string).replace('42', ''))[1]['msg']
              // })
              this.messList.push(new MessageItem(JSON.parse((value as string).replace('42', ''))[1]['msg'],false))
              this.scroller.scrollEdge(Edge.Bottom)
            }
          }
        });
        // 3.打开链接(发消息依赖此状态)
        this.ws.on("open", (err: BusinessError, value: Object) => {
          console.log("hmsocket on open, status:" + JSON.stringify(value));
        })
        // 4.监听关闭(发消息依赖此状态)
        this.ws.close((err: BusinessError) => {
          if (!err) {
            console.log("hmsocket close success");
          } else {
            console.log("hmsocket close fail, err is " + JSON.stringify(err));
          }
        });
      } catch (error) {
        promptAction.showToast({ message: error.message })
      }
    }
  }

  async sendMessage() {
    if (!this.contentStr) {
      return
    }
    this.sendIng = true
    try {
      const testData: [string, MessageStruct] = ['message', {
        timestamp: Date.now(),
        msg: this.contentStr
      }]
      const data = JSON.stringify(testData)
      this.ws.send('42' + data, (err: BusinessError, value: boolean) => {
        if (!err) {
          console.log("hmsocket send success");
        } else {
          console.log("hmsocket send fail, err:" + JSON.stringify(err))
        }
      })
      this.messList.push(new MessageItem(this.contentStr))
      this.scroller.scrollEdge(Edge.Bottom)
      this.sendIng = false
      this.contentStr = ""
    } catch (error) {
      AlertDialog.show({ message: JSON.stringify(error) })
    }

  }

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Column() {
        Row() {
          Text("🙉 老婆大人")
          // .minFontSize(50)
        }
        .width('100%')
        .height(50)
        .justifyContent(FlexAlign.Center)

        List({ space: 20, scroller: this.scroller }) {
          ForEach(this.messList, (item: MessageItem) => {
            ListItem() {
              // 一左一右
              MessageCom({
                item, delMessage: (id: string) => {
                  // 删除消息
                  const index = this.messList.findIndex(item => item.id === id)
                  this.messList.splice(index, 1)
                }
              })
            }
          })
        }
        .padding({
          bottom: 80
        })
        .layoutWeight(1)
      }.height('100%')

      // 消息列表
      Row({ space: 20 }) {
        TextInput({ text: this.contentStr })
          .layoutWeight(1)
          .backgroundColor(Color.White)
          .borderRadius(2)
          .height(40)
          .onChange((value) => {
            this.contentStr = value
          })
          .onSubmit(() => {
            if (this.contentStr) {
              this.sendMessage()
            }
          })
        Button() {
          Row() {
            if (this.sendIng) {
              LoadingProgress()
                .width(20)
                .height(20)
                .color('#fff')
                .margin({
                  right: 4
                })
            } else {
              Text("发送")
                .fontColor("#fff")
            }
          }
        }
        .onClick(async () => {
          if (!this.sendIng) {
            await this.sendMessage()
          }
        })
        .enabled(!!this.contentStr)
        .height(30)
        .width(60)
      }
      .width('100%')
      .backgroundColor('#f6f6f6')
      .height(70)
      .padding({
        left: 10,
        right: 10
      })
    }
    .backgroundColor('#ededed')
    .height('100%')
  }
}

@Component
struct MessageCom {
  @Prop
  item: Partial<MessageItem> = new MessageItem('')
  @State
  showMenu: boolean = false
  delMessage: (id: string) => void = () => {
  }

  @Builder
  getDelContent() {
    Button("删除")
      .backgroundColor(Color.White)
      .fontColor("#0c2803")
      .height(30)
      .onClick(() => {
        this.delMessage(this.item.id!)
      })
  }

  build() {
    Row() {
      Image(this.item.avatar)
        .height(40)
        .width(40)
        .borderRadius(6)
      Row() {
        Text(this.item.content)
          .backgroundColor(this.item.self ? "#8bec73" : Color.White)
          .fontColor("#0c2803")
          .padding(10)
          .lineHeight(24)
          .margin({
            left: 10,
            right: 10
          })
          .borderRadius(5)
          .gesture(
            LongPressGesture()
              .onAction(() => {
                this.showMenu = true // 显示菜单
              })
          )
          .bindPopup(this.showMenu, {
            builder: this.getDelContent,
            placement: Placement.Top,
            onStateChange: (event) => {
              this.showMenu = event.isVisible
            }
          })
      }.layoutWeight(5)
      .justifyContent(this.item.self ? FlexAlign.End : FlexAlign.Start)

      Text().layoutWeight(1)
    }
    .width('100%')
    .padding({
      left: 20,
      right: 20
    })
    .alignItems(VerticalAlign.Top)
    .direction(this.item.self ? Direction.Rtl : Direction.Ltr)
  }
}


export class MessageItem {
  content: string = "" // 消息内容
  avatar: ResourceStr = "" // 用户头像
  username: string = "" // 用户名称
  self: boolean = false // 是不是用户自己
  timestamp: number = Date.now() // 消息收到时的时间戳
  id: string = util.generateRandomUUID() // 唯一的id
  constructor(content: string, self: boolean = true) {
    this.content = content
    this.self = self
    this.avatar = self?'https://foruda.gitee.com/avatar/1705232317138324256/1759638_itcast_panpu_1705232317.png':'http://toutiao.itheima.net/images/user_head.jpg'
  }
}

interface ResponseResult<T> {
  data: T
  message: string
}

interface Token {
  token: string
}

interface MessageStruct {
  msg: string
  timestamp: number
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值