实时聊天 Vue + Vuex + sockjs-client + stompjs进行websocket连接

知识点

WebSocket介绍

WebSocket 是一种在 Web 应用中实现实时通信的方法,它可以在客户端和服务器端之间建立长连接,实现实时消息传递。

SockJS

SockJS 是一个 JavaScript 库,用于在浏览器和 Web 服务器之间建立实时通信连接。它提供了一个 WebSocket 的备选方案,并兼容多种浏览器和 Web 服务器。SockJS 会自动检测浏览器是否支持 WebSocket,如果不支持,则会自动降级为其他协议(如 long polling、iframe、JSONP 等)

STOMP

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。
简单地说,stomp是一个用于client之间进行异步消息传输的简单文本协议

开发环境

"sockjs-client": "^1.5.1",
"stompjs": "^2.3.3",
"vue": "^2.6.10",
"vuex": "^3.6.2"
// 本地Node版本号
node v14.7.0

功能实现

安装应用

npm install sockjs-client --save
npm install stompjs --save

在vuex中创建

const call = {
  state: {
    websocketUrl: 'https://app.lzzgh.org.cn/lz72hourApi',
    stompClient: '', // websocket
    sendContent: "",//发送的数据
    userPhone: "",//给对方发送消息对方的userPhone
    chatType: "",//聊天的方式 1:单聊 2:群聊
    activitySign:false,//是否参加活动
    groupContent:"",//群聊发送数据
	groupCPContent:"", // cp小屋的
    clears:false,//清空消息
  },
  getters: {
    getWebsocketUrl: state => {
      return state.websocketUrl
    },
    getWebsocket: state => {
      return state.stompClient
    },
    getSendContent: state => {
      return state.sendContent
    },
    getGroupContent: state => {
      return state.groupContent
    },
		getGroupCPContent: state => {
		  return state.groupCPContent
		},
    getUserPhone: state => {
      return state.userPhone
    },
    getChatType: state => {
      return state.chatType
    },
    getActivitySign:state =>{
      return state.activitySign
    },
    getClears:state =>{
      return state.clears
    }
  },
  mutations: {
    setMyWebsocket(state, stompClient) {
      state.stompClient = stompClient
    },
    setMySendContent(state, sendContent) {
      state.sendContent = sendContent
    },
    setMyGroupContent(state, groupContent) {
      state.groupContent = groupContent
    },
		setMyGroupCPContent(state, groupCPContent) {
		  state.groupCPContent = groupCPContent
		},
    setMyUserPhone(state, userPhone) {
      state.userPhone = userPhone
    },
    setMyChatType(state, chatType) {
      state.chatType = chatType
    },
    setMyActivitySign(state){
      state.activitySign = !state.activitySign
    },
    setMyClears(state){
      state.clears = !state.clears
    }
  },
  actions: {
    setWebsocket({ commit }, stompClient) {
      commit('setMyWebsocket', { stompClient })
    },
    setSendContent({ commit }, sendContent) {
      commit('setMySendContent', { sendContent })
    },
    setGroupContent({ commit }, groupContent) {
      commit('setMyGroupContent', { groupContent })
    },
		setGroupCPContent({ commit }, groupCPContent) {
		  commit('setMyGroupCPContent', { groupCPContent })
		},
    setUserPhone({ commit }, userPhone) {
      commit('setMyUserPhone', { userPhone })
    },
    setChatType({ commit }, chatType) {
      commit('setMyChatType', { chatType })
    }
  }
}
export default call

vue中的引入、监听、实例化与收发、订阅消息

引入组件

import { mapGetters } from "vuex";
import Video from "@/components/video"; //播放
import { imgPreview } from "@/utils/comperssImage.js"; //压缩图片
import { judgeTime } from "@/utils/base.js"; //时间显示
import { Picker } from "emoji-mart-vue"; //引入表情组件
import SockJS from  'sockjs-client';
import  Stomp from 'stompjs';

实例化与订阅

init(){ 
      axios.defaults.baseURL=this.websocketUrl
      axios.defaults.headers={ "X-Access-Token": Vue.ls.get(ACCESS_TOKEN)}
      if (typeof WebSocket === "undefined") {
        alert("您的浏览器不支持socket");
      } else {
        console.log(this.websocketUrl)
        // 实例化
        let socket = new SockJS(this.websocketUrl + "/jeecg-boot/websocket",undefined, { timeout: 10000 });
        this.stompClient = Stomp.over(socket);
        this.$store.commit("setMyWebsocket", this.stompClient);
        let that = this;
        console.log(Vue.ls.get(ACCESS_TOKEN))
        // 订阅
        this.stompClient.connect(
          { "X-Access-Token": Vue.ls.get(ACCESS_TOKEN) }, 
          function (frame) {
            that.isConnent = true;
            that.stompClient.subscribe(
              "/topic/" + that.chatRoomId,
              function (response) {
                let data = response.body;
                that.$store.commit("setMyGroupCPContent", data);
                console.log("小屋" + response.body);
              }
            );
          }
        )
      }
    },

计算属性

computed: {
    ...mapGetters({
      userInfo: "getUserInfo", //获取用户信息
      websocketUrl: "getWebsocketUrl",
      // stompClient: "getWebsocket", //获取websocket
      // sendContent: "getSendContent", //获取实时聊天信息
      sendContent: "getGroupCPContent", //获取实时聊天信息
    }),
  },

监听收到消息

watch: {
    sendContent() {
      debugger
      Toast.clear();
      let datas = JSON.parse(this.sendContent);
      if (datas.code == 500) {
        Dialog.alert({
          message: datas.message,
        }).then(() => {
          // on close
          this.$router.go(-1);
        });
        return;
      }
      this.message = "";
      this.quoteText = "";
      this.quoteValue = null;
      this.smileShow = false;
      let mesItem = datas.result;
      mesItem.isOprate = false;
      if (mesItem.messageType == "6") {
        mesItem.poster = this.getImgView(mesItem.messageValue);
      }
      if (mesItem.quoteValue) {
        mesItem.quoteValue = JSON.parse(mesItem.quoteValue);
      }
      if (mesItem.delFlag == "1") {
        if (
          mesItem.memberId == this.myInfo.id &&
          mesItem.chatRoomId == this.chatRoomId
        ) {
          Dialog.alert({
            message: "您已被踢出群聊,暂时不能参与会话!",
            confirmButtonText: "返回到首页",
          }).then(() => {
            // on close
            this.$router.replace("/index");
          });
        }
      } else {
        let time = mesItem.createTime;
        if (mesItem.chatRoomId == this.chatRoomId) {
          if (
            this.talkArr.length > 0 &&
            new Date(time.replace(/\-/g, "/")).getTime() -
              new Date(
                this.talkArr[this.talkArr.length - 1].createTime.replace(
                  /\-/g,
                  "/"
                )
              ).getTime() <=
              5 * 60 * 1000
          ) {
            this.talkArr[this.talkArr.length - 1].mesList.push(mesItem);
          } else {
            this.talkArr.push({
              createTime: time,
              time: judgeTime(time),
              mesList: [mesItem],
            });
          }
          console.log(this.talkArr)
          debugger
          this.seeMsg();
          if (!this.isLoading) {
            this.scrollBottom();
          }
        }
      }
    },
  },

封装的发送消息的公共方法

sendFun(messageType, messageValue, quoteValue) {
      // console.log(messageValue)
      Toast.loading({
        duration: 0, // 持续展示 toast
        message: "发送中...",
        forbidClick: true,
      });
      let date = new Date();
      let createTime = date.getTime();
      let prm=JSON.stringify({
          chatRoomId: this.chatRoomId, //群聊id
          messageType: messageType, //1文字,5图片,6视频,7求手机号、8求微信号、9发手机号、10发微信号
          messageValue: messageValue, //发送信息
          // userPhone: this.myInfo.userPhone, //用户手机号
          // fullName: this.myInfo.fullName, //用户名
          // userIcon: this.myInfo.userIcon, //用户头像
          // memberId: this.myInfo.id, //用户id
          memberId: sessionStorage.getItem('clientId'), //用户id
          // createTime: createTime, //创建时间
          // sex: this.myInfo.sex, //性别
          quoteValue: JSON.stringify(quoteValue), //引用的内容
        })
      console.log(prm)
      this.stompClient.send(
        "/app/chatRoom",
        {},
        prm
      );
    },

发送消息

//发送消息
    sendMsg() {
      const that = this;
      if (this.message) {
        this.sendFun("1", this.message, this.quoteValue);
        // that.message = "";
        // that.quoteText = "";
        // that.quoteValue = null;
      } else {
        Toast("内容不能为空!");
      }
    },

完整的代码

聊天的完整代码,包括websocket的应用,(实例化,存vuex,订阅,收发消息),表情,上传图片,视频,历史记录,时间显示,长链接的建立与销毁等

<template>
  <div class="groupChatInf">
    <!-- <NavBar :title="title"></NavBar> -->
    <div class="chatInf">
      <div class="chatBox" id="chat">
        <van-pull-refresh v-model="isLoading" @refresh="onRefresh">
          <div class="chatOne" v-for="(item, i) in talkArr" :key="i">
            <div class="date">{{ item.time }}</div>
            <div v-for="(items, is) in item.mesList" :key="is">
              <div
                class="welcome"
                v-html="welcome"
                v-if="welcome && items.messageType == '4'"
              ></div>
              <div v-if="items.messageType != '4'">
                <div
                  class="chatMine"
                  v-if="items.userPhone == myInfo.userPhone"
                >
                  <div
                    class="chatImg"
                    v-if="
                      items.messageType != '12' && items.messageType != '15'
                    "
                  >
                    <van-image
                      :src="items.userIcon"
                      fit="cover"
                      v-if="items.userIcon"
                      ><template v-slot:loading>
                        <van-loading type="spinner" size="20" />
                      </template>
                      <template v-slot:error>加载失败</template></van-image
                    >
                    <img
                      src="~@/assets/img/redGirl.png"
                      alt=""
                      v-else-if="items.memberId == 9999999"
                      class="imgs"
                    />
                    <img
                      src="~@/assets/img/girlHeadImg.png"
                      alt=""
                      v-else-if="items.sex == 'F'"
                      class="imgs"
                    />
                    <img
                      src="~@/assets/img/boyHeadImg.png"
                      alt=""
                      class="imgs"
                      v-else
                    />
                  </div>
                  <div
                    class="chatName"
                    v-if="
                      items.messageType != '12' && items.messageType != '15'
                    "
                  ><i v-if="items.isAdmin == '1'"
                      ><img src="~@/assets/img/manage2.png"
                    /></i>
                    <i v-if="items.isAdmin == '2'"
                      ><img src="~@/assets/img/manage1.png"
                    /></i>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '1'">
                    <div
                      class="chatText"
                      @touchstart="touchstart(items)"
                      @touchend="touchend"
                    >
                      {{ items.messageValue }}
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                      <span @click="recallChat(items)">撤回</span>
                    </div>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '5'">
                    <div
                      class="chatImage"
                      @click="imgPreview(items.messageValue)"
                    >
                      <img :src="items.messageValue" alt="" />
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                      <span @click="recallChat(items)">撤回</span>
                    </div>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '6'">
                    <div class="chatVideo">
                      <video
                        :src="items.messageValue"
                        :poster="items.poster"
                      ></video>
                      <div
                        class="chatmask"
                        @click="videoPlay(items.messageValue, items.poster)"
                      >
                        <img src="~@/assets/bofang.png" alt="" />
                      </div>
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                      <span @click="recallChat(items)">撤回</span>
                    </div>
                  </div>
                  <div
                    class="chatvalueDiv"
                    v-if="items.quoteValue && items.messageType != '12'"
                  >
                    <div
                      class="quoteValue"
                      v-if="items.quoteValue.messageType == '1'"
                    >
                      @{{
                        items.quoteValue.fullName +
                        items.quoteValue.messageValue
                      }}
                    </div>
                    <div
                      class="quoteImg"
                      v-else-if="items.quoteValue.messageType == '5'"
                    >
                      <p>@{{ items.quoteValue.fullName }}</p>
                      <img
                        :src="items.quoteValue.messageValue"
                        alt=""
                        class="qimgs"
                        @click="imgPreview(items.quoteValue.messageValue)"
                      />
                    </div>
                    <div
                      class="quoteImg"
                      v-else-if="items.quoteValue.messageType == '6'"
                    >
                      <p>@{{ items.quoteValue.fullName }}</p>
                      <div class="quoteVideo">
                        <video
                          :src="items.quoteValue.messageValue"
                          :poster="items.quoteValue.poster"
                        ></video>
                        <div
                          class="chatmask"
                          @click="
                            videoPlay(
                              items.quoteValue.messageValue,
                              items.quoteValue.poster
                            )
                          "
                        >
                          <img src="~@/assets/bofang.png" alt="" />
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="chatRecall" v-if="items.messageType == '12'">
                    消息已撤回 <a @click="reEdit(items)">重新编辑</a>
                  </div>
                </div>
                <div class="chatOther" v-else>
                  <div
                    class="chatImg"
                    @click="toPerson(items.memberId)"
                    v-if="
                      items.messageType != '12' && items.messageType != '15'
                    "
                  >
                    <van-image
                      :src="items.userIcon"
                      fit="cover"
                      v-if="items.userIcon"
                      ><template v-slot:loading>
                        <van-loading type="spinner" size="20" />
                      </template>
                      <template v-slot:error>加载失败</template></van-image
                    >
                    <img
                      src="~@/assets/img/girlHeadImg.png"
                      alt=""
                      v-else-if="items.sex == 'F'"
                      class="imgs"
                    />
                    <img
                      src="~@/assets/img/boyHeadImg.png"
                      alt=""
                      class="imgs"
                      v-else
                    />
                    <img
                      src="~@/assets/img/woman.png"
                      alt=""
                      class="sex"
                      v-if="items.sex == 'F'"
                    />
                    <img
                      src="~@/assets/img/man.png"
                      alt=""
                      class="sex"
                      v-if="items.sex == 'M'"
                    />
                  </div>
                  <div
                    class="chatName"
                    v-if="
                      items.messageType != '12' && items.messageType != '15'
                    "
                  >
                    {{ items.fullName
                    }}<i v-if="items.isAdmin == '1'"
                      ><img src="~@/assets/img/manage2.png"
                    /></i>
                    <i v-if="items.isAdmin == '2'"
                      ><img src="~@/assets/img/manage1.png"
                    /></i>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '1'">
                    <div
                      class="chatText"
                      @touchstart="touchstart(items)"
                      @touchend="touchend"
                    >
                      {{ items.messageValue }}
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                    </div>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '5'">
                    <div
                      class="chatImage"
                      @click="imgPreview(items.messageValue)"
                    >
                      <img :src="items.messageValue" alt="" />
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                    </div>
                  </div>
                  <div class="chatvalueDiv" v-if="items.messageType == '6'">
                    <div class="chatVideo">
                      <video
                        :src="items.messageValue"
                        :poster="items.poster"
                      ></video>
                      <div
                        class="chatmask"
                        @click="videoPlay(items.messageValue, items.poster)"
                      >
                        <img src="~@/assets/bofang.png" alt="" />
                      </div>
                    </div>
                    <div
                      class="chatOprateMask"
                      v-if="items.isOprate"
                      @click="closeMask(items)"
                    ></div>
                    <div class="chatOprate" v-if="items.isOprate">
                      <span @click="quoteChat(items)">引用</span>
                      <span @click="delChat(items)">删除</span>
                      <span @click="copyChat(items)">复制</span>
                    </div>
                  </div>
                  <div
                    class="chatvalueDiv"
                    v-if="items.quoteValue && items.messageType != '12'"
                  >
                    <div
                      class="quoteValue"
                      v-if="items.quoteValue.messageType == '1'"
                    >
                      @{{
                        items.quoteValue.fullName +
                        items.quoteValue.messageValue
                      }}
                    </div>
                    <div
                      class="quoteImg"
                      v-else-if="items.quoteValue.messageType == '5'"
                    >
                      <p>@{{ items.quoteValue.fullName }}</p>
                      <img
                        :src="items.quoteValue.messageValue"
                        alt=""
                        class="qimgs"
                        @click="imgPreview(items.quoteValue.messageValue)"
                      />
                    </div>
                    <div
                      class="quoteImg"
                      v-else-if="items.quoteValue.messageType == '6'"
                    >
                      <p>@{{ items.quoteValue.fullName }}</p>
                      <div class="quoteVideo">
                        <video
                          :src="items.quoteValue.messageValue"
                          :poster="items.quoteValue.poster"
                        ></video>
                        <div
                          class="chatmask"
                          @click="
                            videoPlay(
                              items.quoteValue.messageValue,
                              items.quoteValue.poster
                            )
                          "
                        >
                          <img src="~@/assets/bofang.png" alt="" />
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="chatRecall" v-if="items.messageType == '12'">
                    {{ items.fullName }}撤回了一条消息
                  </div>
                  <div class="chatRecall" v-if="items.messageType == '15'">
                    <a>{{ items.createBy }}</a
                    >加入了群聊
                  </div>
                </div>
              </div>
            </div>
          </div>
        </van-pull-refresh>
      </div>
    </div>
    <div class="sendBox">
      <div class="sendBoxTop">
        <form>
          <!-- @click="toMember" -->
          <span class="sendMember" >
            <img src="~@/assets/img/member.png" alt="" />
          </span>
          <van-field
            class="sendInf"
            v-model="message"
            :border="false"
            style="border: 0px;"
            @keyup.enter.native="sendMsg"
          />
          <div class="quoteMessage" v-if="quoteText">
            <p class="quoteText">{{ quoteText }}</p>
            <img
              src="~@/assets/img/mclose.png"
              alt=""
              class="quoteClose"
              @click="quoteClose"
            />
          </div>
          <span class="sendExpression" @click="smile">
            <img src="~@/assets/img/smile.png" alt="" />
          </span>
          <span class="sendBtn" @click="sendMsg"
            ><img src="~@/assets/img/sendBtn.png" alt=""
          /></span>
        </form>
      </div>
      <div class="emojiBox" @select="addEmoji" v-if="smileShow">
        <div class="emojiDiv">
          <span
            v-for="(item, index) in emojiList"
            :key="'e-' + index"
            @click="chooseEmoji(item)"
            >{{ item.emoji }}</span
          >
        </div>
      </div>
      <!-- <picker
        :include="['people', 'Smileys']"
        :showSearch="false"
        :showPreview="false"
        :showCategories="false"
        @select="addEmoji"
        v-if="smileShow"
      /> -->
      <div class="sendBoxBtm" v-if="!smileShow">
        <div class="sendOprate">
          <img src="~@/assets/img/micon1.png" alt="" />
          <van-uploader
            :after-read="afterRead"
            upload-icon="plus"
            :before-read="beforeRead"
            accept="image/png,image/jpg,image/jpeg,video/mp4,video/mov"
            :max-count="1"
          />
        </div>
        <div class="sendOprate">
          <img src="~@/assets/img/micon2.png" alt="" />
          <!--是苹果手机直接调用摄像头-->
          <van-uploader
            :after-read="afterReadVideo"
            upload-icon="plus"
            :before-read="beforeReadVideo"
            accept="image/png,image/jpg,image/jpeg,video/mp4,video/mov"
            :max-count="1"
            capture="camera"
            v-if="isIos"
          />
          <!--不是苹果手机先调起相册-->
          <van-uploader
            :after-read="afterReadVideo"
            upload-icon="plus"
            :before-read="beforeReadVideo"
            accept="image/png,image/jpg,image/jpeg,video/mp4,video/mov"
            :max-count="1"
            v-if="!isIos"
          />
        </div>
        <div class="sendOprate">
          <img src="~@/assets/img/nowx.png" alt="" />
        </div>
        <div class="sendOprate">
          <img src="~@/assets/img/nophone.png" alt="" />
        </div>
      </div>
    </div>
    <Video
      :videoPath="videoPath"
      v-if="videoShow"
      @videoFun="videoFun"
      :posterPath="posterPath"
    ></Video>
  </div>
</template>

<script>
import {
  Image as vanImage,
  Toast,
  PullRefresh,
  Field,
  Loading,
  Dialog,
  ImagePreview,
  Uploader,
} from "vant";
import { mapGetters } from "vuex";
import Video from "@/components/video"; //播放
import { imgPreview } from "@/utils/comperssImage.js"; //压缩图片
import { judgeTime } from "@/utils/base.js"; //时间显示
import { Picker } from "emoji-mart-vue"; //引入表情组件
import SockJS from  'sockjs-client';
import  Stomp from 'stompjs';
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import axios from 'axios'

export default {
  name: "GroupChat",
  components: {
    [vanImage.name]: vanImage,
    [Field.name]: Field,
    [PullRefresh.name]: PullRefresh,
    [Loading.name]: Loading,
    [Uploader.name]: Uploader,
    Video,
    Picker,
  },
  props:{
    records:{
      type: Object,
      default: ()=>({})
    }
  },
  data() {
    return {
      stompClient:"",
      message: "", //发送内容
      talkArr: [], //聊天信息
      title: "聊天", //标题
      isToday: false, //是否是今天
      isLoading: false, //是否在加载中
      params: {
        chatRoomId: "", //群聊id
        pageNo: 1, //页数
        pageSize: 50, //每页数据数量
        oldMesId: "", //最后一条消息id
      }, //群聊查询历史数据参数
      chatRoomId: "", //群聊id
      allPage: 0, //历史消息总页数
      myInfo: {}, //个人信息
      welcome: "", //欢迎信息
      isFirst: true, //是否首次进入
      quoteText: "", //引用的文字
      videoShow: false, //播放器插件是否出现
      videoPath: "", //视频路径
      posterPath: "", //默认图路径
      touch: null, //长按方法定时器
      reMessage: "", //要重新编辑的信息
      fileList: [], //上传图片视频返回值
      videoList: [], //拍摄返回值
      isIos: false, //是否是苹果手机
      quoteValue: null, //引用的详细信息
      isClick: true, //操作按钮是否可以点击
      smileShow: false, //
      imgUrl:'',
    };
  },
  computed: {
    ...mapGetters({
      userInfo: "getUserInfo", //获取用户信息
      websocketUrl: "getWebsocketUrl",
      // stompClient: "getWebsocket", //获取websocket
      // sendContent: "getSendContent", //获取实时聊天信息
      sendContent: "getGroupCPContent", //获取实时聊天信息
    }),
  },
  watch: {
    sendContent() {
      debugger
      Toast.clear();
      let datas = JSON.parse(this.sendContent);
      if (datas.code == 500) {
        Dialog.alert({
          message: datas.message,
        }).then(() => {
          // on close
          this.$router.go(-1);
        });
        return;
      }
      this.message = "";
      this.quoteText = "";
      this.quoteValue = null;
      this.smileShow = false;
      let mesItem = datas.result;
      mesItem.isOprate = false;
      if (mesItem.messageType == "6") {
        mesItem.poster = this.getImgView(mesItem.messageValue);
      }
      if (mesItem.quoteValue) {
        mesItem.quoteValue = JSON.parse(mesItem.quoteValue);
      }
      if (mesItem.delFlag == "1") {
        if (
          mesItem.memberId == this.myInfo.id &&
          mesItem.chatRoomId == this.chatRoomId
        ) {
          Dialog.alert({
            message: "您已被踢出群聊,暂时不能参与会话!",
            confirmButtonText: "返回到首页",
          }).then(() => {
            // on close
            this.$router.replace("/index");
          });
        }
      } else {
        let time = mesItem.createTime;
        if (mesItem.chatRoomId == this.chatRoomId) {
          if (
            this.talkArr.length > 0 &&
            new Date(time.replace(/\-/g, "/")).getTime() -
              new Date(
                this.talkArr[this.talkArr.length - 1].createTime.replace(
                  /\-/g,
                  "/"
                )
              ).getTime() <=
              5 * 60 * 1000
          ) {
            this.talkArr[this.talkArr.length - 1].mesList.push(mesItem);
          } else {
            this.talkArr.push({
              createTime: time,
              time: judgeTime(time),
              mesList: [mesItem],
            });
          }
          console.log(this.talkArr)
          debugger
          this.seeMsg();
          if (!this.isLoading) {
            this.scrollBottom();
          }
        }
      }
    },
  },
  created() {
    this.imgUrl=this.websocketUrl+'/jeecg-boot/sys/common/static'
    const that = this;
    const UA = navigator.userAgent;
    const isIpad = /(iPad).*OS\s([\d_]+)/.test(UA);
    const isIpod = /(iPod)(.*OS\s([\d_]+))?/.test(UA);
    const isIphone = !isIpad && /(iPhone\sOS)\s([\d_]+)/.test(UA);
    this.isIos = isIpad || isIpod || isIphone;
    if (this.$route.query.activityName) {
      this.title = this.$route.query.activityName;
    }
    this.type = this.$route.query.type;
    if (this.$route.query.activityId) {
      this.chatRoomId = this.$route.query.activityId;
      this.params.chatRoomId = this.$route.query.activityId;
    }
    console.log(this.records)
    this.chatRoomId = this.records.chatId;
    this.params.chatRoomId = this.records.chatId;
    this.init()
    // this.getpersonfo();
    //查看未读消息
    this.seeMsg();
    //获取历史消息
    this.getHistory();
    this.getEmoji();
  },
  // mounted() {},
  destroyed() {
    // 取消订阅
    this.stompClient.unsubscribe('/topic/'+this.chatRoomId)
    // 销毁连接
    this.stompClient.disconnect()
  }, 
  methods: {
    // 初始化
    init(){
      axios.defaults.baseURL=this.websocketUrl
      axios.defaults.headers={ "X-Access-Token": Vue.ls.get(ACCESS_TOKEN)}
      if (typeof WebSocket === "undefined") {
        alert("您的浏览器不支持socket");
      } else {
        console.log(this.websocketUrl)
        let socket = new SockJS(this.websocketUrl + "/jeecg-boot/websocket",undefined, { timeout: 10000 });
        this.stompClient = Stomp.over(socket);
        this.$store.commit("setMyWebsocket", this.stompClient);
        let that = this;
        console.log(Vue.ls.get(ACCESS_TOKEN))
        this.stompClient.connect(
          { "X-Access-Token": Vue.ls.get(ACCESS_TOKEN) }, 
          function (frame) {
            that.isConnent = true;
            that.stompClient.subscribe(
              "/topic/" + that.chatRoomId,
              function (response) {
                let data = response.body;
                that.$store.commit("setMyGroupCPContent", data);
                console.log("小屋" + response.body);
              }
            );
          }
        )
      }
    },
    //获取表情包
    getEmoji() {
      axios.get("/jeecg-boot/api/emoji/emojiList")
        .then((res) => {
          if (res.code == 200) {
            this.emojiList = res.result;
          }
        })
        .catch(() => {});
    },
    //获取个人资料
    getpersonfo() {
      axios.get("/jeecg-boot/api/member/memberDetails")
        .then((res) => {
          if (res.code == 200) {
            this.myInfo = res.result;
          }
        })
        .catch(() => {});
    },
    //发送消息
    sendMsg() {
      const that = this;
      if (this.message) {
        this.sendFun("1", this.message, this.quoteValue);
        // that.message = "";
        // that.quoteText = "";
        // that.quoteValue = null;
      } else {
        Toast("内容不能为空!");
      }
    },
    //查看未读消息
    seeMsg() {
      axios.get("/jeecg-boot/api/releaseLog/seePcReleaseLog", {params:{
        type: 2,
        chatRoomId: this.chatRoomId,
      }})
        .then((res) => {})
        .catch(() => {});
    },
    //刷新历史消息
    onRefresh() {
      this.getHistory();
      // if (this.params.pageNo <= this.allPage) {
      //   this.getHistory();
      // } else {
      //   this.isLoading = false;
      // }
    },
    //查看历史消息
    getHistory() {
      const that = this;
      axios.get("/jeecg-boot/api/memberMessage/historyCpMsgList", {params:this.params} )
        .then((res) => {
          // console.log(res);
          res=res.data
          if (res.result.records.length == 0) {
            if (!this.isFirst) {
              Toast("没有更多数据了");
            }
          }
          res.result.records.forEach((item) => {
            let mesItem = item;
            let time = mesItem.createTime;
            if (mesItem.messageType == "6") {
              mesItem.poster = that.getImgView(mesItem.messageValue);
            }
            mesItem.isOprate = false;
            if (mesItem.quoteValue) {
              mesItem.quoteValue = JSON.parse(mesItem.quoteValue);
            }
            if (mesItem.chatRoomId == this.chatRoomId) {
              if (mesItem.messageType == "4") {
                this.welcome = mesItem.messageValue.replace(/\r\n/g, "<br>");
                this.welcome = this.welcome.replace(/\n/g, "<br>");
              }
              if (this.talkArr.length > 0) {
                let time1 = new Date(time.replace(/\-/g, "/")).getTime();
                let time2 = new Date(
                  that.talkArr[0].createTime.replace(/\-/g, "/")
                ).getTime();
                let diff = 0;
                if (parseInt(time1) >= parseInt(time2)) {
                  diff = parseInt(time1) - parseInt(time2);
                } else {
                  diff = parseInt(time2) - parseInt(time1);
                }
                if (diff <= 5 * 60 * 1000) {
                  that.talkArr[0].mesList.unshift(mesItem);
                } else {
                  that.talkArr.unshift({
                    createTime: time,
                    time: judgeTime(time),
                    mesList: [mesItem],
                  });
                }
              } else {
                that.talkArr.unshift({
                  createTime: time,
                  time: judgeTime(time),
                  mesList: [mesItem],
                });
              }
            }
          });
          // Toast("刷新成功");
          this.isLoading = false;
          this.allPage = res.result.pages;
          this.params.oldMesId =
            res.result.records[res.result.records.length - 1].id;
          if (this.isFirst) {
            that.isFirst = false;
            that.scrollBottom();
          }
        })
        .catch(() => {});
    },
    //去个人主页
    toPerson(id) {
      if (id) {
        this.$router.push({ path: "/person", query: { id: id } });
      }
    },
    //去聊天室信息页面
    toMember() {
      this.$router.push({
        path: "/groupMember",
        query: { activityId: this.chatRoomId },
      });
    },
    scrollBottom() {
      this.$nextTick((res) => {
        let div = document.getElementById("chat");
        div.scrollTop = div.scrollHeight;
      });
    },

    //选择表情
    smile() {
      this.smileShow = !this.smileShow;
    },
    //查看大图
    imgPreview(src) {
      // ImagePreview([src]);
    },
    //视频播放
    videoPlay(path, poster) {
      this.videoPath = path;
      this.posterPath = poster;
      this.videoShow = true;
    },
    //
    videoFun(visible) {
      this.videoShow = visible;
      this.posterPath = "";
      this.videoPath = "";
    },
    /* 视频取第一秒截图 */
    getImgView(text) {
      return text + "?x-oss-process=video/snapshot,t_1000";
    },
    // 在屏幕上时触发
    touchstart(items) {
      clearTimeout(this.touch); //再次清空定时器,防止重复注册定时器
      this.touch = setTimeout(() => {
        items.isOprate = true;
        this.$forceUpdate();
      }, 800);
    },
    //离开屏幕时触发
    touchend() {
      clearTimeout(this.touch); //再次清空定时器,防止重复注册定时器
    },
    //点击任意地方关闭操作
    closeMask(items) {
      items.isOprate = false;
      this.$forceUpdate();
    },
    //重新编辑
    reEdit() {
      this.message = this.reMessage;
    },
    //引用
    quoteChat(items) {
      if (items.messageType == "1") {
        this.quoteText = "@" + items.fullName + " " + items.messageValue;
      } else if (items.messageType == "5") {
        this.quoteText = "@" + items.fullName + " [图片]";
      } else if (items.messageType == "6") {
        this.quoteText = "@" + items.fullName + " [视频]";
      }
      this.quoteValue = items;
      items.isOprate = false;
      this.$forceUpdate();
    },
    //清楚引用
    quoteClose() {
      this.quoteText = "";
      this.quoteValue = null;
    },
    //删除
    delChat(items) {
      if (this.isClick) {
        this.isClick = false;
        this.$fetch("/jeecg-boot/api/memberMessage/delMessageLog", {
          messageLogId: items.id,
        })
          .then((res) => {
            if (res.code == 200) {
              Toast("消息删除成功");
              items.isOprate = false;
              items.delFlag = "1";
              this.$forceUpdate();
            } else {
              Toast(res.message);
            }
            this.isClick = true;
          })
          .catch(() => {
            this.isClick = true;
          });
      }
    },
    //复制
    copyChat(items) {
      this.copeText(items);
    },
    //撤回
    recallChat(items) {
      if (this.isClick) {
        this.isClick = false;
        this.$fetch("/jeecg-boot/api/memberMessage/delMessageLog", {
          messageLogId: items.id,
          messageType: 12,
        })
          .then((res) => {
            if (res.code == 200) {
              Toast("消息撤回成功");
              items.isOprate = false;
              items.messageType = "12";
              this.$forceUpdate();
            } else {
              Toast(res.message);
            }
            this.isClick = true;
          })
          .catch(() => {
            this.isClick = true;
          });
      }
    },
    //复制消息
    copeText(items) {
      let that = this;
      // 数字没有 .length 不能执行selectText 需要转化成字符串
      const textString = items.messageValue.toString();
      let input = document.querySelector("#copy-input");
      if (!input) {
        input = document.createElement("input");
        input.id = "copy-input";
        input.readOnly = "readOnly"; // 防止ios聚焦触发键盘事件
        input.style.position = "absolute";
        input.style.left = "-1000px";
        input.style.zIndex = "-1000";
        document.body.appendChild(input);
      }
      input.value = textString;
      // ios必须先选中文字且不支持 input.select();
      this.selectText(input, 0, textString.length);
      if (document.execCommand("copy")) {
        document.execCommand("copy");
        console.log("复制成功");
        Toast("复制成功!");
        items.isOprate = false;
        that.$forceUpdate();
      } else {
        console.log("不兼容");
      }
      input.blur();
    },
    // input自带的select()方法在苹果端无法进行选择,所以需要自己去写一个类似的方法
    // 选择文本。createTextRange(setSelectionRange)是input方法
    selectText(textbox, startIndex, stopIndex) {
      if (textbox.createTextRange) {
        // ie
        const range = textbox.createTextRange();
        range.collapse(true);
        range.moveStart("character", startIndex); // 起始光标
        range.moveEnd("character", stopIndex - startIndex); // 结束光标
        range.select(); // 不兼容苹果
      } else {
        // firefox/chrome
        textbox.setSelectionRange(startIndex, stopIndex);
        textbox.focus();
      }
    },
    // 上传图片
    async afterRead(fileList) {
      Toast.loading({
        duration: 0, // 持续展示 toast
        message: "发送中...",
        forbidClick: true,
      });
      let that = this;
      let formData = new FormData(); //构造一个 FormData,把后台需要发送的参数添加
      if (fileList.file.type.indexOf("video") == 0) {
        formData.append("file", fileList.file); //接口需要传的参数
        formData.append("biz", fileList.file.name.split(".")[0]);
        await axios.post("/jeecg-boot/sys/common/uploadCp", formData)
          .then((res) => {
            Toast.clear();
            if (res.data.code == 0) {
              let imgsrc = res.data.message;
              that.sendFun("6", imgsrc);
            } else {
              Toast("发送失败");
            }
          });
      } else {
        imgPreview(fileList.file, async (files) => {
          // console.log(files)
          formData.append("file", files); //接口需要传的参数
          formData.append("biz", files.name.split(".")[0]);
          await axios.post("/jeecg-boot/sys/common/uploadCp", formData)
            .then((res) => {
              Toast.clear();
              if (res.data.code == 0) {
                let imgsrc = this.imgUrl+res.data.message;
                that.sendFun("5", imgsrc);
              } else {
                Toast("发送失败");
              }
            });
        });
      }
    },
    beforeRead(fileList) {
      if (
        fileList.type.indexOf("image") != 0 &&
        fileList.type.indexOf("video") != 0
      ) {
        Toast("只能上传图片(jpg,jpeg,png)、视频(mp4,mov)");
        return false;
      }
      let fileListLength = fileList.length ? fileList.length : 1;
      if (fileListLength + this.fileList.length > 1) {
        Toast("最多同时上传一张图片或视频");
        return false;
      }
      return true;
    },
    // 拍摄
    async afterReadVideo(fileList) {
      Toast.loading({
        duration: 0, // 持续展示 toast
        message: "发送中...",
        forbidClick: true,
      });
      let that = this;
      let formData = new FormData(); //构造一个 FormData,把后台需要发送的参数添加
      if (fileList.file.type.indexOf("video") == 0) {
        formData.append("file", fileList.file); //接口需要传的参数
        formData.append("biz", fileList.file.name.split(".")[0]);
        await axios.post("/jeecg-boot/sys/common/uploadCp", formData)
          .then((res) => {
            Toast.clear();
            if (res.data.code == 0) {
              let imgsrc = res.data.message;
              that.sendFun("6", imgsrc);
            } else {
              Toast("发送失败");
            }
          });
      } else {
        imgPreview(fileList.file, async (files) => {
          // console.log(files)
          formData.append("file", files); //接口需要传的参数
          formData.append("biz", files.name.split(".")[0]);
          await axios.post("/jeecg-boot/sys/common/uploadCp", formData)
            .then((res) => {
              Toast.clear();
              if (res.data.code == 0) {
                let imgsrc =this.imgUrl+ res.data.message;
                that.sendFun("5", imgsrc);
              } else {
                Toast("发送失败");
              }
            });
        });
      }
    },
    beforeReadVideo(fileList) {
      if (
        fileList.type.indexOf("image") != 0 &&
        fileList.type.indexOf("video") != 0
      ) {
        Toast("只能上传图片(jpg,jpeg,png)、视频(mp4,mov)");
        return false;
      }
      let fileListLength = fileList.length ? fileList.length : 1;
      if (fileListLength + this.fileList.length > 1) {
        Toast("最多同时上传一张图片或视频");
        return false;
      }
      return true;
    },
    //发送方法
    sendFun(messageType, messageValue, quoteValue) {
      // console.log(messageValue)
      Toast.loading({
        duration: 0, // 持续展示 toast
        message: "发送中...",
        forbidClick: true,
      });
      let date = new Date();
      let createTime = date.getTime();
      let prm=JSON.stringify({
          chatRoomId: this.chatRoomId, //群聊id
          messageType: messageType, //1文字,5图片,6视频,7求手机号、8求微信号、9发手机号、10发微信号
          messageValue: messageValue, //发送信息
          // userPhone: this.myInfo.userPhone, //用户手机号
          // fullName: this.myInfo.fullName, //用户名
          // userIcon: this.myInfo.userIcon, //用户头像
          // memberId: this.myInfo.id, //用户id
          memberId: sessionStorage.getItem('clientId'), //用户id
          // createTime: createTime, //创建时间
          // sex: this.myInfo.sex, //性别
          quoteValue: JSON.stringify(quoteValue), //引用的内容
        })
      console.log(prm)
      this.stompClient.send(
        "/app/chatRoom",
        {},
        prm
      );
    },
    //添加表情
    addEmoji(e) {
      this.message += e.native;
    },
    //选择表情
    chooseEmoji(item) {
      this.message += item.emoji;
    },
  },
  beforeDestroy() {
    Toast.clear();
  },
  updated() {},
};
</script>

<style lang="scss" scoped>
.groupChatInf {
  position: relative;
  .chatBox {
    // position: relative;
    height: calc(100vh - 320px);
    overflow-y: auto;
  }
  /deep/.van-pull-refresh {
    // min-height: 300px;
    min-height: calc(100vh - 320px);
    .van-pull-refresh__track {
      padding: 0px 10px;
    }
  }
  // ::v-deep .van-pull-refresh {
  //   min-height: 300px;
  // }
  .welcome {
    text-align: center;
    font-size: 14px;
    color: #666;
    padding: 6px 10px;
  }
  .chatInf {
    width: 100%;
    padding: 0px 0px 70px 0px;
    .chatOne {
      .date {
        text-align: center;
        line-height: 30px;
        font-size: 12px;
        color: #999;
      }
      .chatMine,
      .chatOther {
        position: relative;
        margin-bottom: 10px;
        padding: 0px 50px;
        min-height: 40px;
        position: relative;
        .chatImg {
          width: 38px;
          height: 38px;
          position: absolute;
          top: 0px;
          .imgs {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            overflow: hidden;
          }
          .van-image {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            overflow: hidden;
          }
          .sex {
            width: 15px;
            position: absolute;
            right: -4px;
            bottom: 0px;
          }
        }
        .chatText {
          padding: 8px 13px;
          background: #d8d8d8;
          font-size: 14px;
          color: #333;
          line-height: 1.7;
          border-radius: 6px;
          position: relative;
        }
        .chatName {
          font-size: 12px;
          color: #666;
          line-height: 20px;
          img {
            width: 11px;
            position: relative;
            top: -1px;
            margin-right: 3px;
          }
        }
        .chatImage,
        .chatVideo {
          width: 124px;
          height: 124px;
          overflow: hidden;
        }
        .chatImage {
          img {
            width: 100%;
            height: 100%;
            object-fit: cover;
          }
        }
        .chatVideo {
          position: relative;
          video {
            width: 100%;
            height: 100%;
          }
          .chatmask {
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            position: absolute;
            top: 0;
            left: 0;
            img {
              width: 40px;
              height: 40px;
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
            }
          }
        }
        .chatvalueDiv {
          position: relative;
        }
        .chatvalueDiv:after {
          content: "";
          display: block;
          clear: both;
        }
        .quoteValue {
          padding: 5px 10px;
          font-size: 10px;
          color: #8e8e93;
          border-radius: 4px;
          background: #eeeeef;
          margin-top: 5px;
          text-overflow: -o-ellipsis-lastline;
          overflow: hidden;
          text-overflow: ellipsis;
          display: -webkit-box;
          -webkit-line-clamp: 2;
          line-clamp: 2;
          -webkit-box-orient: vertical;
        }
        .chatOprate {
          width: auto;
          height: 28px;
          background: #fff;
          position: absolute;
          bottom: -34px;
          z-index: 120;
          border-radius: 4px;
          display: flex;
          span {
            font-size: 13px;
            color: #333;
            line-height: 28px;
            width: 40px;
            text-align: center;
            margin: 0 10px;
          }
        }
        .chatOprateMask {
          width: 100%;
          height: 100%;
          position: fixed;
          top: 0;
          left: 0;
          background: rgba(255, 255, 255, 0);
          z-index: 111;
        }
        .quoteImg {
          padding: 5px 10px;
          border-radius: 4px;
          background: #eeeeef;
          margin-top: 5px;
          p {
            font-size: 10px;
            color: #8e8e93;
          }
          .qimgs {
            width: 90px;
            height: 60px;
            object-fit: cover;
            margin-left: 5px;
          }
          .quoteVideo {
            width: 90px;
            height: 60px;
            position: relative;
            margin-left: 5px;
            video {
              width: 100%;
              height: 100%;
            }
            .chatmask {
              width: 100%;
              height: 100%;
              background: rgba(0, 0, 0, 0.5);
              position: absolute;
              top: 0;
              left: 0;
              img {
                width: 30px;
                height: 30px;
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
              }
            }
          }
        }
      }
      .chatMine {
        .chatImg {
          right: 0px;
        }
        .chatText {
          float: right;
          background: #ff4e7b;
          border: 1px #ff4e7b solid;
          color: #fff;
        }
        .chatName {
          text-align: right;
        }
        .quoteValue {
          float: right;
        }
        .chatImage,
        .chatVideo {
          border-radius: 6px 0 6px 6px;
          float: right;
        }
        .chatOprate {
          right: 0;
        }
        .chatOprate:after {
          content: "";
          position: absolute;
          width: 0px;
          height: 0px;
          line-height: 0px; /*为了防止ie下出现题型*/
          border-bottom: 5px solid #fff;
          border-left: 5px solid transparent;
          border-right: 5px solid transparent;
          right: 10px;
          top: -5px;
        }
        .quoteImg {
          float: right;
          p {
            float: left;
          }
          .qimgs,
          .quoteVideo {
            float: right;
          }
        }
      }
      .chatOther {
        .chatImg {
          left: 0px;
        }
        .chatText {
          float: left;
          background: #fff;
          // border: 1px #eee solid;
        }
        .chatImage,
        .chatVideo {
          border-radius: 0 6px 6px 6px;
          float: left;
        }
        .quoteValue {
          float: left;
        }
        .chatOprate {
          left: 0;
        }
        .chatOprate:after {
          content: "";
          position: absolute;
          width: 0px;
          height: 0px;
          line-height: 0px; /*为了防止ie下出现题型*/
          border-bottom: 5px solid #fff;
          border-left: 5px solid transparent;
          border-right: 5px solid transparent;
          left: 10px;
          top: -5px;
        }
        .quoteImg {
          float: left;
          p {
            float: left;
          }
          .qimgs,
          .quoteVideo {
            float: right;
          }
        }
      }
      .chatMine:after,
      .chatOther:after {
        content: "";
        display: block;
        clear: both;
      }
    }
  }
  .sendBox {
    // width: 400px;
    width: 100%;
    background: #fff;
    position: absolute;
    z-index: 99;
    left: 0px;
    margin: 0 auto;
    bottom: -20px;
    border-top: 1px #eee solid;
    .sendBoxTop {
      min-height: 50px;
      width: 100%;
      padding: 6px 60px 6px 60px;
      .sendMember {
        width: 40px;
        height: 40px;
        position: absolute;
        top: 5px;
        left: 10px;
        img {
          width: 24px;
          height: 24px;
          position: absolute;
          top: 8px;
          left: 8px;
        }
      }
      .sendInf {
        width: 100%;
        height: 40px;
        background: #f5f5f5;
        border-radius: 20px;
        padding-left: 10px;
      }
      .sendBtn {
        width: 60px;
        height: 50px;
        padding: 5px 10px;
        position: absolute;
        top: 0px;
        right: 0px;
        img {
          width: 40px;
        }
      }
    }
    .sendBoxBtm {
      width: 100%;
      height: 42px;
      display: flex;
      .sendOprate {
        flex: 1;
        height: 100%;
        align-items: center;
        justify-content: center;
        position: relative;
        img {
          padding: 5px;
          width: 34px;
          height: 34px;
          display: block;
          margin: 4px auto;
        }
        ::v-deep .van-uploader {
          width: 34px;
          height: 34px;
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          .van-uploader__upload {
            width: 34px;
            height: 34px;
            margin: 0;
            opacity: 0;
          }
        }
      }
    }
    .sendExpression {
      width: 30px;
      height: 30px;
      position: absolute;
      top: 12px;
      right: 65px;
      padding: 3px;
      img {
        width: 24px;
      }
    }
    .quoteMessage {
      display: inline-block;
      background: #f4f4f4;
      border-radius: 6px;
      padding: 5px 30px 5px 8px;
      position: relative;
      margin-top: 6px;
      .quoteText {
        font-size: 10px;
        color: #8e8e93;
        line-height: 1.5;
        text-overflow: -o-ellipsis-lastline;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        -webkit-box-orient: vertical;
      }
      .quoteClose {
        width: 24px;
        height: 24px;
        padding: 5px;
        position: absolute;
        right: 5px;
        top: 50%;
        transform: translateY(-50%);
      }
    }
  }
  .chatRecall {
    text-align: center;
    font-size: 12px;
    color: #8e8e93;
    padding: 6px 0px;
    line-height: 20px;
    a {
      color: #ff0021;
    }
  }
  .rejectCol {
    color: #ee6262;
  }
  ::v-deep .emoji-mart {
    width: 100% !important;
    height: 160px;
    border: none;
    .emoji-mart-category-label {
      display: none;
    }
  }
  .emojiBox {
    width: 100%;
    height: 160px;
    overflow: auto;
    .emojiDiv {
      display: flex;
      flex-wrap: wrap;
      padding: 0 10px;
      span {
        padding: 3px;
        width:32px;
        height:32px;
        text-align:center;
        line-height:32px;
      }
    }
  }
}
::v-deep .van-field__control{
  margin-top: 8px;
  border: 0px;
  width: calc(100% - 40px);
  background-color: transparent;
}
::v-deep .van-field__control:focus{
  outline: none !important;
}
</style>

相关推荐

  1. webScoket实时通讯聊天

    2024-07-17 17:20:02       54 阅读
  2. netty+websocket实现简易聊天

    2024-07-17 17:20:02       30 阅读
  3. fastapi实现websocket在线聊天

    2024-07-17 17:20:02       58 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 17:20:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 17:20:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 17:20:02       58 阅读
  4. Python语言-面向对象

    2024-07-17 17:20:02       69 阅读

热门阅读

  1. C#泛型使用介绍

    2024-07-17 17:20:02       27 阅读
  2. 06 - FFmpeg 提取 YUV420P 视频裸数据

    2024-07-17 17:20:02       18 阅读
  3. 识别视频中的人数并统计出来

    2024-07-17 17:20:02       23 阅读
  4. 超详细Python教程——异步任务和定时任务

    2024-07-17 17:20:02       20 阅读
  5. opencv—常用函数学习_“干货“_7

    2024-07-17 17:20:02       22 阅读
  6. Nginx

    2024-07-17 17:20:02       19 阅读
  7. 大模型日报 2024-07-15

    2024-07-17 17:20:02       19 阅读
  8. 使用 CSS 实现透明效果

    2024-07-17 17:20:02       20 阅读
  9. HTML常见标签(一)

    2024-07-17 17:20:02       23 阅读
  10. 网络编程:IO多路复用(五个IO模型)

    2024-07-17 17:20:02       23 阅读
  11. 【防抖工具库 es-toolkit】

    2024-07-17 17:20:02       26 阅读