H5聊天页面-uniapp

发布于 2025-03-19  262 次阅读



第一次写出这么完美的聊天页面(自我感觉良好),有点激动,也不知道在小程序或者在app有没有问题,反正解决了当期的问题
一是键盘遮挡输入框问题(这次没遇到,小程序篇遇到了小程序聊天
二是加载历史数据的时候,会把当前浏览的数据顶下去(反转页面来解决的)
三是问题二搞出来,就是只有少量数据的时候,消息在底部而不是在顶部
废话不多说,直接上代码

<template>
  <view class="hotel_chat">
<!--    :style="{height: (contentViewHeight-bottomHeight) + 'px'}"-->
    <view class="chat_content" id="chat_content">
<!--      :style="{height: (contentViewHeight-bottomHeight) + 'px'}"   :scroll-into-view="scrollToView"-->
      <scroll-view class="scroll_view" :lower-threshold="50" :upper-threshold="50"  :scroll-anchoring="true" :scroll-top="currentTop" id="scrollview" scroll-y="true" @scrolltolower="scrolltolower" @scroll="scrollList" @touchstart="onTouchStart">

        <view class="chat_list" id="msglistview">
          <view class="list_content" v-for="(item,index) in chatList" :key="item.id">

            <view class="list_right" v-if="item.fromUid !=  fromUid" :id="'msg' + item.id">
              <view class="list_avatar">
                <image src="https://htres.78plat.com/miniv3/static/user_default.png"></image>
              </view>
              <div class="content_right">
                <view class="list_context">
                  <view class="list_title">{{item.guestName}}</view>
                  <view class="time_text">{{handlerMsgTime(item.timestamp)}}</view>
                </view>
                <view class="right_bg" v-if="item.content&&item.chatType=='text'">
                  <text class="text_info">{{item.content}}</text>
                </view>
                <view class="audio_box right_bg" v-if="item.content&&item.chatType=='audio'" @click="playAudio(item,index)">
                  <!-- 语音 -->
                  <image v-if="index == currentIndex" src="/static/user_audio_gif.gif" mode=""></image>
                   <image v-else src="/static/user_audio_icon.png" mode=""></image>
                  <view class="audio_text">{{item.duration || 0}}s</view>

                </view>
              </div>

            </view>
            <!-- 右边 -->
            <view class="list_left" v-else :id="'msg' + item.id">
              <view class="list_avatar">
                <image src="https://htres.78plat.com/miniv3/static/hotel_default.png"></image>
              </view>
              <div class="content_left">
                <view class="list_context">
                  <view class="list_title">{{item.guestName}}</view>
                  <view class="time_text">{{handlerMsgTime(item.timestamp)}}</view>
                </view>
                <view class="left_bg" v-if="item.content&&item.chatType=='text'">
                  <text class="text_info">{{item.content}}</text>
                </view>
                <view class="audio_box left_bg" :style="{width: widthPercent(item.duration)+'%'}" v-if="item.content&&item.chatType=='audio'"  @click="playAudio(item,index)">
                  <!-- 语音 -->
                  <view class="audio_text">{{item.duration || 0}}s</view>
                  <image v-if="index == currentIndex" src="/static/hotel_audio_gif.gif" mode=""></image>
                  <image v-else src="/static/hotel_audio_icon.png" mode=""></image>
                </view>
              </div>

            </view>
            <!-- 左边 -->
          </view>
        </view>
      </scroll-view>
    </view>


<!--    :style="{bottom: felxHeight + 'px'}"-->
    <view class="chat_below" id="below" >
<!--      <view class="below_left" @click="Tcyy">常用语</view>-->
      <view class="below_right">
        <input :adjust-position="false" v-model="msgValue" :focus="focus" @focus="inputFocus" @blur="inputBlur" placeholder="请输入内容"/>
        <view class="rg_btn" @click="sendClick">发送</view>
      </view>
    </view>
  </view>
</template>

<script>
import socketIO from '@/common/utils/newSocket'
export default {
  data() {
    return {
      chatList: [

      ],
      newChatList: [
        {
          name: '小明',
          content: '你好啊,小红同学你好啊你好啊你好啊你好啊你好啊你好啊你好啊你好啊',
          time: '1742196395',
          id: 1,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 2,
          userId: '456789'
        },
        {
          name: '小明',
          content: '你好啊,小红同学你好啊你好啊你好啊你好啊你好啊',
          time: '1742196395',
          id: 3,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 4,
          userId: '456789'
        },
        {
          name: '小明',
          content: '你好啊,小红同学',
          time: '1742196395',
          id: 5,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 6,
          userId: '456789'
        },
        {
          name: '小明',
          content: '你好啊,小红同学',
          time: '1742196395',
          id: 7,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 8,
          userId: '456789'
        },
        {
          name: '小明',
          content: '你好啊,小红同学',
          time: '1742196395',
          id: 9,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 10,
          userId: '456789'
        },
        {
          name: '小明',
          content: '你好啊,小红同学',
          time: '1742196395',
          id: 11,
          userId: '123456'
        },
        {
          name: '小红',
          content: '你好啊,小红同学',
          time: '1742196398',
          id: 12,
          userId: '456789'
        },
      ],
      scrollToView: '',
      scrollViewHeight: '',
      contentViewHeight: '',
      bottomHeight: '',
      felxHeight: '',
      msgValue: '',
      focus: false,
      socketCase: null,
      shopId: '',
      merchantId: '',
      mobile: '',
      guestName: '',
      roomNo: '',
      shopName: '',
      userId: '',
      userPhone: '',
      userName: '',
      chatUrl:'',
      pageCount: 0,
      pageNo: 1,
      pageSize: 10,
      fromUid: '',
      toUid: '',
      currentIndex:null,
      audioContext: null, // 存储音频实例
      currentTop: 0,
      oldScrollTop: 0
    };
  },
  onLoad(options) {
    this.shopId= options.shopId || '320'
    this.merchantId= options.merchantId || '110060146'
    this.mobile= options.mobile || '18365211931'
    this.guestName= options.guestName || '李在明'
    this.roomNo= options.roomNo || '888'
    this.shopName = options.shopName || '合肥市格林豪泰爱电竞'
    this.userId = options.userId || '4685'
    this.userPhone= options.userPhone || ''
    this.userName= options.userName || ''
    const res = uni.getSystemInfoSync();   //获取手机可使用窗口高度 api为获取系统信息同步接口
    this.scrollViewHeight = res.windowHeight;   //像素   因为给出的是像素高度px
    this.contentViewHeight = res.windowHeight; //像素   因为给出的是像素高度px

//获取自定义头部和消息框高度
    let that = this;
    this.nextTick(()=>{    //获取头部的高度
      let query = uni.createSelectorQuery()
      // query.select('#nav').boundingClientRect()
      query.select('#below').boundingClientRect()
      query.exec((res) => {
        console.log(res,"获取的res")
        // that.navHeight = res[0].height;
        that.bottomHeight = res[0].height
      })
    })


//获取当前可视区域高度
    this.contentViewHeight = this.contentViewHeight-this.bottomHeight;
  },
  onHide() {
    if(this.socketCase) {
      this.socketCase.Close()
      this.socketCase = null
    }
  },
  onUnload() {
    if(this.socketCase) {
      this.socketCase.Close()
      this.socketCase = null
    }
  },
  onShow() {
    this.pageNo = 1
    this.chatList = []
    this.getWebSocket()
  },
  mounted() {
    let that = this;
    //H5解决办法
    // #ifdef H5
    window.onresize = () => {
      let that = this;
      this.nowPhoneHeight = window.innerHeight; //h5
      if(this.nowPhoneHeightnextTick(()=>{
            that.scrollToView = 'msg' + that.chatList[that.chatList.length - 1].id;
          })
        },50)
      }else{
        //手机键盘被关闭了。
        console.log("手机键盘被关闭了")
        this.felxHeight = 0;
        this.contentViewHeight = this.scrollViewHeight;
        this.scrollToView = '';
        setTimeout(()=>{
          that.nextTick(()=>{
            that.scrollToView = 'msg' + that.chatList[that.chatList.length - 1].id;
          })
        },50)
      }
    }
    // #endif

    //解决app端--(安卓)--点击软键盘上关闭按钮以后留白问题
    // #ifdef APP
    uni.onKeyboardHeightChange(res => { //监听第三方软键盘关闭的时候恢复原始状态
      if(res.height == 0){
        that.felxHeight = 0;
        that.contentViewHeight = that.scrollViewHeight;
        // this.scrollToView = '';
        that.scrollToView = 'msg' + that.chatList[that.chatList.length - 1].id;
      }
    })
    // #endif
  },
  methods: {
    sendClick(e) {
      let that = this;
      console.log("发送信息")
      // let centerOnj = {
      //   name: '小红',
      //   content: this.msgValue,
      //   time: new Date().getTime(),
      //   id: this.chatList.length + 1,
      //   userId: '456789'
      // }
      // this.chatList.push(centerOnj)
      // return;
      // 完成
      console.log(e, this.msgValue, "=========");
      if (this.msgValue.length <= 0) {
        uni.showToast({
          title: "不能发送空白信息",
          icon: "none",

        })
        return
      }
      //时间间隔处理
      let data = {
        content: this.fixedEncodeURIComponent(this.msgValue),
        messageType: 20,
        userType: 6,
        fromUid: this.fromUid,
        toUid: this.toUid,
        channel: this.channel,
        mobile: this.mobile,
        guestName: this.guestName,
        roomNo: this.roomNo,
        chatType: 'text',
        duration: '',
        userId: this.userId,
        userName: this.userName,

      };
      // if (this.socketOpen) {
      //    console.log("发消息不行",JSON.stringify(data));
      //    uni.sendSocketMessage({
      //        data: JSON.stringify(data),
      //        success(res) {
      //            console.log(res, "发送成功==");
      //        },
      //        complete(other) {
      //            console.log(other, "消息毁掉==");
      //        }
      //    });
      // }
      // 发送给服务器消息
      // this.socketObj.sendMsg(JSON.stringify(data));
      this.socketCase.send(data);

      let centerData = {
        content: this.msgValue,
        messageType: 20,
        userType: 6,
        fromUid: this.fromUid,
        toUid: this.toUid,
        channel: this.channel,
        mobile: this.mobile,
        guestName: this.guestName,
        roomNo: this.roomNo,
        time: this.formatCurrentTime(),
        timestamp: new Date().getTime(),
        chatType: 'text',
        duration: '',
        userId: this.userId,
        userName: this.userName,
      }
      // this.unshiftmsg.push(centerData);
      // 因为整个数据是反过来的,所以是加在数据最前面
      this.chatList.push(centerData);

      this.msgValue = ''
      this.backTop()
    },
    getMessage(msgData) {
      // console.log(msgData, "接收到的消息");
      if(msgData.data) {
        console.log(msgData.data)
        console.log((msgData.data.person.mobile + msgData.data.roomNo) != (this.mobile + this.roomNo),"咋不对",msgData.data.person.mobile+msgData.data.roomNo,"roomNo",this.mobile + this.roomNo);

        if((msgData.data.person.mobile + msgData.data.roomNo) != (this.mobile + this.roomNo) ) {
          return
        }

        if (msgData.data.fromUid != this.fromUid&&msgData.data.fromUid == this.toUid) {
          msgData.data.content = decodeURIComponent(msgData.data.content)
          msgData.data.time = this.formatCurrentTime(msgData.data.timestamp)
          this.chatList.push(msgData.data);
          this.backTop()
        }

      }



    },
    // 连接准备接口
    async getWebSocket() {
      let that = this
      let param = {
        shopId: this.shopId,
        merchantId: this.merchantId,
        mobile: this.mobile,
        guestName: this.guestName,
        userId: this.userId,
        userPhone: this.userPhone,
        userName: this.userName,
        roomNo: this.roomNo,
        shopName: this.shopName,
        serviceFor: 'mini',
        userType: 6,


      }
      const res = await this.Http.indexService.getWebSocket(param)
      console.log(res, "聊天");
      if(res.retdata) {

        let ret = res.retdata
        that.fromUid = ret.fromUid // 发送方标识
        that.toUid = ret.toUid // 接收方标识
        that.userType = ret.userType // 发送方类型
        that.channel = ret.channel // 消息通道标识
        that.chatUrl = ret.chatWsPath || ''
        that.socketCase = new socketIO({}, 16000, ret.chatWsPath)
        that.socketCase.connectSocketInit()

        this.getHistoryChat()
        // 接收数据
        uni.on("getPositonsOrder", (res) => {
          console.log(res, "监听不到信息");
          this.getMessage(res)
        })
      }

    },
    async readChat() {
      let param = {
        mobile: this.mobile,
        roomNo: this.roomNo,
        guestName: this.guestName,
        channel: this.channel,
        fromUid: this.toUid,
        toUid: this.fromUid,
        serviceFor: 'mini',
        computer: ''

      }
      const res = await this.Http.indexService.readChat(param)
      console.log(res, "置未读为已读");
      if(res.retcode=='0') {
        console.log("置空成功")
      } else {
        console.log("置空失败")
      }
    },
    async getHistoryChat() {
      let that = this
      if (that.pageNo == 1) {
        that.chatList = []
      }
      let param = {
        shopId: this.shopId,
        pageNo: this.pageNo,
        pageSize: this.pageSize,
        mobile: this.mobile,
        roomNo: this.roomNo,
        channel: this.channel,
        fromUid: this.fromUid,
        toUid: this.toUid,
        userType: this.userType,
        serviceFor: "mini"

      }
      const res = await this.Http.indexService.getHistoryChat(param)
      console.log(res, "历史消息");
      if(res.retdata) {
        this.pageCount = res?.retdata?.pageCount || 0
        let ret = res?.retdata?.data || []
        ret = ret.reverse();
        // that.unshiftmsg = [...ret, ...that.unshiftmsg]
        // ret = ret.reverse();

        that.chatList = [...ret,...that.chatList]

        if (that.pageNo == 1) {
          this.backTop()
          // 跳转到最后一条数据 与前面的:id进行对照
          // this.nextTick(function() {
          //   this.currentTop = 0
          //   this.scrollToView = 'msg' + (this.chatList.length - 1)
          // })
        }
        this.readChat()
      }
    },
    // 触摸开始:说明可能是手势滑动
    onTouchStart() {
      console.log("说明可能是手势滑动")
      if(this.focus) {
        this.focus = false
      }
      // this.isTouching = true;
    },
    scrolltolower(e) {
      console.log(e,"滚动到顶部")
      if (this.pageNo < this.pageCount) {
        this.pageNo += 1
        this.getHistoryChat()
      }
    },
    scrollList(e) {
      // console.log(e,"页面滚动了====")
      this.oldScrollTop = e.detail.scrollTop
    },
    playAudio(item, index) {
      let path = item.content;

      // 如果已有音频在播放,先停止并销毁
      if (this.audioContext) {
        this.audioContext.stop();
        this.audioContext.destroy();
        this.audioContext = null;
      }

      // 重新创建音频实例
      this.audioContext = uni.createInnerAudioContext();
      this.audioContext.src = path;
      this.audioContext.autoplay = true; // 自动播放
      this.audioContext.obeyMuteSwitch = false;

      // 监听播放开始
      this.audioContext.onPlay(() => {
        this.currentIndex = index;
        console.log("开始播放:", path);
      });

      // 监听播放结束
      this.audioContext.onEnded(() => {
        this.stopAudio();
      });

      // 监听错误
      this.audioContext.onError((err) => {
        console.error("播放错误:", err);
        this.stopAudio();
      });

      // **关键点:确保 play() 触发**
      this.audioContext.play();

    },
    stopAudio() {
      if (this.audioContext) {
        this.audioContext.stop();
        this.audioContext.destroy();
        this.audioContext = null;
        this.currentIndex = null;
        console.log("音频已停止");
      }
    },
    // 语音长度随语音秒数变化
    widthPercent(value=0) {
      // 语音小于10秒 都返回最小长度15%
      if (value < 2) {
        return 3
      }
      if (value > 30) {
        return 45
      }
      return value / 60 * 100
    },
    //监听input获取焦点事件和失去焦点时间
    inputFocus(e) { //触发焦点
      this.scrollToView = ''
      this.felxHeight = e.detail.height;    //获取软键盘高度修改输入框定位
      this.contentViewHeight = this.contentViewHeight - e.detail.height;    //可视高度减去软键盘高度
      this.backTop()
      this.focus = true
    },
    inputBlur(e){   //失去焦点
      this.scrollToView = ''
      this.felxHeight = 0;  //失去焦点恢复原位
      this.contentViewHeight = this.scrollViewHeight;
      this.backTop()
      this.focus = false

    },
    fixedEncodeURIComponent(str) {
      return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16).toUpperCase();
      });
    },
    backTop() {
      let that = this;
      console.log("回到底部")
      this.currentTop = this.oldScrollTop
      setTimeout(()=>{
        that.nextTick(function(){
          this.currentTop = 0
          that.scrollToView = 'msg' + that.chatList?.[that.chatList.length - 1]?.id;
        })
      },50)
      // that.scrollToView = ''
      // setTimeout(()=>{
      //   that.nextTick(function(){
      //     that.scrollToView = 'msg' + that.chatList?.[that.chatList.length - 1]?.id;
      //   })
      // },50)
    },
    formatCurrentTime(time = '') {
      const now = time ? new Date(time) :new Date();
      return now.getTime();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, '0');
      const day = String(now.getDate()).padStart(2, '0');
      const hours = String(now.getHours()).padStart(2, '0');
      const minutes = String(now.getMinutes()).padStart(2, '0');
      const seconds = String(now.getSeconds()).padStart(2, '0');

      return `{year}-{month}-{day}{hours}:{minutes}:{seconds}`;
    },
    handlerMsgTime(timeValueData) {
      // console.log(timeValueData)
      // 如果是字符串,先转成数字
      let timeValue = (new Date(timeValueData)).getTime();
      var timeNew = new Date().getTime(); // 当前时间
      // console.log('传入的时间', timeValue, timeNew)
      var timeDiffer = parseFloat(timeNew) - parseFloat(timeValue); // 与当前时间误差
      // console.log(timeDiffer,"timeDiffer", parseFloat(timeNew),"==",parseFloat(timeValue))
      // console.log('时间差', timeDiffer)
      var returnTime = "";
      // if (timeDiffer <= 60000) {
      //   // 一分钟内
      //   returnTime = "刚刚";
      // } else if (timeDiffer > 60000 && timeDiffer < 3600000) {
      //   // 1小时内
      //   returnTime = Math.floor(timeDiffer / 60000) + "分钟前";
      // } else if (
      // timeDiffer >= 3600000 &&

      if (timeDiffer < 86400000 && this.isYestday(timeValue) == false) {
        // 今日
        returnTime = this.formatTime(timeValue).substr(11, 5);
      } else if (timeDiffer > 3600000 && this.isYestday(timeValue) == true) {
        // 昨天
        returnTime = "昨天 " + this.formatTime(timeValue).substr(11, 5);
      } else if (timeDiffer > 86400000 && timeDiffer <= 518400000) {
        // 星期几
        returnTime =
            this.getWeeken(timeValue) +
            " " +
            this.formatTime(timeValue).substr(11, 5);
      } else if (
          timeDiffer > 86400000 &&
          this.isYestday(timeValue) === false &&
          this.isYear(timeValue) === true
      ) {
        // 今年
        returnTime = this.formatTime(timeValue).substr(5, 11);
      } else if (
          timeDiffer > 86400000 &&
          this.isYestday(timeValue) === false &&
          this.isYear(timeValue) === false
      ) {
        // 不属于今年
        returnTime = this.formatTime(timeValue).substr(0, 16);
      }
      // console.log(returnTime,"=====")
      return returnTime;
    },
    isYear(timeValue) {
      // 是否为今年
      const dateyear = new Date(timeValue).getFullYear();
      const toyear = new Date().getFullYear();
      // console.log(dateyear, toyear)
      if (dateyear == toyear) {
        return true;
      } else {
        return false;
      }
    },
    getWeeken(date) {
      var weekArray = new Array(
          "星期日",
          "星期一",
          "星期二",
          "星期三",
          "星期四",
          "星期五",
          "星期六"
      );
      var week = weekArray[new Date(date).getDay()];
      return week;
    },

    formatTime(date) {
      var t = this.getTimeArray(date);
      return (
          [t[0], t[1], t[2]].map(this.formatNumber).join("-") +
          " " +
          [t[3], t[4], t[5]].map(this.formatNumber).join(":")
      );
    },

    getTimeArray(date) {
      date = new Date(date);
      var year = date.getFullYear();
      var month = date.getMonth() + 1;
      var day = date.getDate();
      var hour = date.getHours();
      var minute = date.getMinutes();
      var second = date.getSeconds();
      return [year, month, day, hour, minute, second].map(this.formatNumber);
    },

    // 转化日期 如2018-7-6 -->(2018-07-06)
    formatNumber(n) {
      n = n.toString();
      return n[1] ? n : "0" + n;
    },

    isYestday(timeValue) {
      // 是否为昨天
      const date = new Date(timeValue);
      const today = new Date();
      if (
          date.getFullYear() == today.getFullYear() &&
          date.getMonth() == today.getMonth()
      ) {
        if (today.getDate() - date.getDate() == 1) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.hotel_chat {
  background: #F7F9FA;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  .chat_content {
    flex: 1;
    overflow: hidden;
    padding: 0 28rpx 0 28rpx;
    box-sizing: border-box;
    .scroll_view {
      height: 100%;
      //overflow: auto;
      transform: rotate(180deg);
      //direction: rtl;
      .chat_list {
        //height: 100%;
        transform: rotate(180deg);
        overflow: auto;
        min-height: 100%;
        .text_info {
          word-break:break-all;
        }
      }
      .list_content {
        margin-bottom: 40rpx;
        &:first-child {
          margin-top: 40rpx;
        }
        .list_avatar {
          flex-shrink: 0;
          width: 68rpx;
          height: 68rpx;
          border-radius: 50%;
          background: #FFFFFF;
          //border: 2rpx solid #FFF;
          image {
            width: 100%;
            height: 100%;
            border-radius: 50%;
          }
        }
        .list_right {
          display: flex;
          .content_right {
            margin-left: 16rpx;
            .audio_box {
              justify-content: flex-start;
              .audio_text {
                margin-left: 20rpx;
              }
            }
          }
          .list_title {
            margin-right: 12rpx;
          }
          .right_bg {
            max-width: 460rpx;
            padding: 12rpx 20rpx;
            box-sizing: border-box;
            border-radius: 8rpx;
            background: #FFF;
            color: #333333;
            font-family: "PingFang SC";
            font-size: 28rpx;
            font-weight: 400;
            width: fit-content;
          }
        }
        .list_left {
          display: flex;
          flex-direction: row-reverse;
          .content_left {
            margin-right: 16rpx;
            display: flex;
            flex-direction: column;
            align-items: flex-end;
            .audio_box {
              justify-content: flex-end;
              .audio_text {
                margin-right: 20rpx;
              }
            }
          }
          .list_context {
            flex-direction: row-reverse;
            .list_title {
              margin-left: 12rpx;
            }
          }
          .left_bg {
            padding: 12rpx 20rpx;
            box-sizing: border-box;
            border-radius: 8rpx;
            background: #0060FF;
            color: #ffffff;
            text-align: right;
            font-family: "PingFang SC";
            font-size: 28rpx;
            font-weight: 400;
            max-width: 460rpx;
            width: fit-content;
            align-self: flex-end;
            flex: 1;
          }
        }
        .list_context {
          display: flex;
          align-items: center;
          margin-bottom: 16rpx;
          .list_title {
            margin-right: 12rpx;
            color: #555b62;
            font-family: "PingFang SC";
            font-size: 20rpx;
            font-weight: 400;
          }
          .time_text {
            color: #9ea5ad;
            font-family: "PingFang SC";
            font-size: 20rpx;
            font-weight: 400;
            line-height: 28rpx;
            word-break:break-all;
          }
        }
        .audio_box {
          display: flex;
          align-items: center;
          font-family: "PingFang SC";
          font-size: 28rpx;
          font-weight: 400;
          //background: #287EFF;
          min-width: 128rpx;
          padding: 18rpx 24rpx;
          box-sizing: border-box;
          border-radius: 8rpx;
          image {
            width: 28rpx;
            height: 28rpx;
            //margin-left: 22rpx;
          }
        }
      }
    }
  }
  .chat_below {
    display: flex;
    align-items: center;
    width: 750rpx;
    padding: 24rpx 28rpx;
    box-sizing: border-box;
    background: #FFFFFF;

    .below_right {
      display: flex;
      align-items: center;
      flex: 1;
      //margin-left: 20rpx;
      input {
        height: 64rpx;
        flex: 1;
        border-radius: 94rpx;
        background: #F7F9FA;
        padding: 0 28rpx;
        box-sizing: border-box;
        color: #333333;
        font-family: "PingFang SC";
        font-size: 28rpx;
        font-weight: 400;
      }
      .rg_btn {
        margin-left: 20rpx;
        width: 132rpx;
        height: 64rpx;
        border-radius: 94rpx;
        background: #0060FF;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #ffffff;
        font-family: "PingFang SC";
        font-size: 28rpx;
        font-weight: 400;
      }
    }
  }
}
</style>

// newSocket.js
class socketIO {
    constructor(data, time, url) {
        this.socketTask = null
        this.is_open_socket = false //避免重复连接
        this.url = url ? url : ''  //连接地址
        this.data = data ? data : null
        this.connectNum = 1 // 重连次数
        this.traderDetailIndex = 100 // traderDetailIndex ==2 重连
        this.accountStateIndex = 100 // accountStateIndex ==1 重连
        this.followFlake = false // followFlake == true 重连
        //心跳检测
        this.timeout = time ? time : 15000 //多少秒执行检测
        this.heartbeatInterval = null //检测服务器端是否还活着
        this.reconnectTimeOut = null //重连之后多久再次重连
    }

    // 进入这个页面的时候创建websocket连接【整个页面随时使用】
    connectSocketInit(data) {
        this.data = data
        this.socketTask = uni.connectSocket({
            url: this.url,
            success: () => {
                console.log("正准备建立websocket中...");
                // 返回实例
                return this.socketTask
            },
        });
        this.socketTask.onOpen((res) => {
            this.connectNum = 1
            console.log("WebSocket连接正常!");
            uni.emit('connectSuccess')
            this.send(data)
            clearInterval(this.reconnectTimeOut)
            clearInterval(this.heartbeatInterval)
            this.is_open_socket = true;
            this.start();
            // 注:只有连接正常打开中 ,才能正常收到消息
            this.socketTask.onMessage((e) => {
                // 字符串转json
                let res = JSON.parse(e.data);
                console.log(e,"获取到消息res---------->", res) // 这里 查看 推送过来的消息
                if (res) {

                    uni.emit('getPositonsOrder', JSON.parse(res)); 
                }
            });
        })
        // 监听连接失败,这里代码我注释掉的原因是因为如果服务器关闭后,和下面的onclose方法一起发起重连操作,这样会导致重复连接
        uni.onSocketError((res) => {
            console.log('WebSocket连接打开失败,请检查!');
            this.socketTask = null
            this.is_open_socket = false;
            clearInterval(this.heartbeatInterval)
            clearInterval(this.reconnectTimeOut)
            uni.off('getPositonsOrder')
            uni.off('connectSuccess')
            if (this.connectNum < 6) {
                uni.showToast({
                    title: `WebSocket连接失败,正尝试第{this.connectNum}次连接`,
                    icon: "none"
                })
                this.reconnect();
                this.connectNum += 1
            } else {
                uni.emit('connectError');
                this.connectNum = 1
            }

        });
        // 这里仅是事件监听【如果socket关闭了会执行】
        this.socketTask.onClose(() => {
            console.log("已经被关闭了-------")
            clearInterval(this.heartbeatInterval)
            clearInterval(this.reconnectTimeOut)
            this.is_open_socket = false;
            this.socketTask = null
            uni.off('getPositonsOrder')
            uni.off('connectSuccess')
            if (this.connectNum < 6) {
                this.reconnect();
            } else {
                uni.$emit('connectError');
                this.connectNum = 1
            }

        })
    }
    // 主动关闭socket连接
    Close() {
        if (!this.is_open_socket) {
            return
        }
        this.socketTask.close({
            success() {
                console.log("SocketTask 关闭成功");
                // uni.showToast({
                //  title: 'SocketTask 关闭成功',
                //  icon: "none"
                // });
            }
        });
    }
    //发送消息
    send(data) {
        // console.log("data---------->", data);
        // 注:只有连接正常打开中 ,才能正常成功发送消息
        if (this.socketTask) {
            this.socketTask.send({
                data: JSON.stringify(data),
                async success() {
                    console.log("消息发送成功");
                },
            });
        }
    }
    //开启心跳检测
    start() {
        this.heartbeatInterval = setInterval(() => {
            this.send({
                "traderid": 10260,
                "type": "Ping"
            });
        }, this.timeout)
    }
    //重新连接
    reconnect() {
        //停止发送心跳
        clearInterval(this.heartbeatInterval)
        //如果不是人为关闭的话,进行重连
        if (!this.is_open_socket && (this.traderDetailIndex == 2 || this.accountStateIndex == 0 || this
            .followFlake)) {
            this.reconnectTimeOut = setInterval(() => {
                this.connectSocketInit(this.data);
            }, 5000)
        }
    }
    /**
     * @description 将 scoket 数据进行过滤 
     * @param {array} array
     * @param {string} type 区分 弹窗 openposition 分为跟随和我的
     */
    arrayFilter(array, type = 'normal', signalId = 0) {
        let arr1 = []
        let arr2 = []
        let obj = {
            arr1: [],
            arr2: []
        }
        arr1 = array.filter(v => v.flwsig == true)
        arr2 = array.filter(v => v.flwsig == false)
        if (type == 'normal') {
            if (signalId) {
                arr1 = array.filter(v => v.flwsig == true && v.sigtraderid == signalId)
                return arr1
            } else {
                return arr1.concat(arr2)
            }
        } else {
            if (signalId > 0) {
                arr1 = array.filter(v => v.flwsig == true && v.sigtraderid == signalId)
                obj.arr1 = arr1
            } else {
                obj.arr1 = arr1
            }
            obj.arr2 = arr2

            return obj
        }
    }
}
module.exports = socketIO

只会写bug的bugming