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

实时聊天

  • 知识点
    • WebSocket介绍
    • SockJS
    • STOMP
  • 开发环境
  • 功能实现
    • 安装应用
    • 在vuex中创建
    • vue中的引入、监听、实例化与收发、订阅消息
      • 引入组件
      • 实例化与订阅
      • 计算属性
      • 监听收到消息
      • 封装的发送消息的公共方法
      • 发送消息
  • 完整的代码

知识点

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: '', // websocketsendContent: "",//发送的数据userPhone: "",//给对方发送消息对方的userPhonechatType: "",//聊天的方式 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.websocketUrlaxios.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() {debuggerToast.clear();let datas = JSON.parse(this.sendContent);if (datas.code == 500) {Dialog.alert({message: datas.message,}).then(() => {// on closethis.$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 closethis.$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)debuggerthis.seeMsg();if (!this.isLoading) {this.scrollBottom();}}}},},

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

sendFun(messageType, messageValue, quoteValue) {// console.log(messageValue)Toast.loading({duration: 0, // 持续展示 toastmessage: "发送中...",forbidClick: true,});let date = new Date();let createTime = date.getTime();let prm=JSON.stringify({chatRoomId: this.chatRoomId, //群聊idmessageType: 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, //用户idmemberId: 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"><divclass="welcome"v-html="welcome"v-if="welcome && items.messageType == '4'"></div><div v-if="items.messageType != '4'"><divclass="chatMine"v-if="items.userPhone == myInfo.userPhone"><divclass="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><imgsrc="~@/assets/img/redGirl.png"alt=""v-else-if="items.memberId == 9999999"class="imgs"/><imgsrc="~@/assets/img/girlHeadImg.png"alt=""v-else-if="items.sex == 'F'"class="imgs"/><imgsrc="~@/assets/img/boyHeadImg.png"alt=""class="imgs"v-else/></div><divclass="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'"><divclass="chatText"@touchstart="touchstart(items)"@touchend="touchend">{{ items.messageValue }}</div><divclass="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'"><divclass="chatImage"@click="imgPreview(items.messageValue)"><img :src="items.messageValue" alt="" /></div><divclass="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><divclass="chatmask"@click="videoPlay(items.messageValue, items.poster)"><img src="~@/assets/bofang.png" alt="" /></div></div><divclass="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><divclass="chatvalueDiv"v-if="items.quoteValue && items.messageType != '12'"><divclass="quoteValue"v-if="items.quoteValue.messageType == '1'">@{{items.quoteValue.fullName +items.quoteValue.messageValue}}</div><divclass="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><divclass="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><divclass="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><divclass="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><imgsrc="~@/assets/img/girlHeadImg.png"alt=""v-else-if="items.sex == 'F'"class="imgs"/><imgsrc="~@/assets/img/boyHeadImg.png"alt=""class="imgs"v-else/><imgsrc="~@/assets/img/woman.png"alt=""class="sex"v-if="items.sex == 'F'"/><imgsrc="~@/assets/img/man.png"alt=""class="sex"v-if="items.sex == 'M'"/></div><divclass="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'"><divclass="chatText"@touchstart="touchstart(items)"@touchend="touchend">{{ items.messageValue }}</div><divclass="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'"><divclass="chatImage"@click="imgPreview(items.messageValue)"><img :src="items.messageValue" alt="" /></div><divclass="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><divclass="chatmask"@click="videoPlay(items.messageValue, items.poster)"><img src="~@/assets/bofang.png" alt="" /></div></div><divclass="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><divclass="chatvalueDiv"v-if="items.quoteValue && items.messageType != '12'"><divclass="quoteValue"v-if="items.quoteValue.messageType == '1'">@{{items.quoteValue.fullName +items.quoteValue.messageValue}}</div><divclass="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><divclass="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><divclass="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-fieldclass="sendInf"v-model="message":border="false"style="border: 0px;"@keyup.enter.native="sendMsg"/><div class="quoteMessage" v-if="quoteText"><p class="quoteText">{{ quoteText }}</p><imgsrc="~@/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"><spanv-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: "", //群聊idpageNo: 1, //页数pageSize: 50, //每页数据数量oldMesId: "", //最后一条消息id}, //群聊查询历史数据参数chatRoomId: "", //群聊idallPage: 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() {debuggerToast.clear();let datas = JSON.parse(this.sendContent);if (datas.code == 500) {Dialog.alert({message: datas.message,}).then(() => {// on closethis.$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 closethis.$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)debuggerthis.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.websocketUrlaxios.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.dataif (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) {// ieconst range = textbox.createTextRange();range.collapse(true);range.moveStart("character", startIndex); // 起始光标range.moveEnd("character", stopIndex - startIndex); // 结束光标range.select(); // 不兼容苹果} else {// firefox/chrometextbox.setSelectionRange(startIndex, stopIndex);textbox.focus();}},// 上传图片async afterRead(fileList) {Toast.loading({duration: 0, // 持续展示 toastmessage: "发送中...",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, // 持续展示 toastmessage: "发送中...",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, // 持续展示 toastmessage: "发送中...",forbidClick: true,});let date = new Date();let createTime = date.getTime();let prm=JSON.stringify({chatRoomId: this.chatRoomId, //群聊idmessageType: 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, //用户idmemberId: 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/44773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉搜索树大冒险:寻找-插入-删除

OK&#xff0c;看我们题目就可知道啦&#xff0c;今天要分享学习的一种数据结构就是二叉搜索树。 内容题目也说了三个大概的&#xff0c;分别是寻找、插入、删除。 讲这个之前呢&#xff0c;那么就先讲讲这个二叉搜索树是何方神圣呢&#xff1f; 二叉搜索树&#xff1a; 又…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(五)-同时支持无人机和eMBB用户数据传输的用例

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

全网最详细单细胞保姆级分析教程(二) --- 多样本整合

上一节我们研究了如何对单样本进行分析,这节我们就着重来研究一下如何对多样本整合进行研究分析! 1. 导入相关包 library(Seurat) library(tidyverse) library(patchwork)2. 数据准备 # 导入单样本文件 dir c(~/Desktop/diversity intergration/scRNA_26-0_filtered_featur…

Linux上如何安装ffmpeg视频处理软件

在Linux上安装ffmpeg需要以下步骤&#xff1a; 更新系统 在开始安装之前&#xff0c;首先需要更新系统以获取最新的软件包列表和版本。在终端中执行以下命令&#xff1a; sudo apt update sudo apt upgrade安装依赖库 ffmpeg依赖于一些库和工具&#xff0c;需要先安装它们。在…

【香橙派 Orange pi AIpro】| 开发板深入使用体验

目录 一. &#x1f981; 写在前面二. &#x1f981; 愉快的安装流程2.1 安装前准备2.2 流程准备2.2.1 烧录镜像2.2.2 开机2.2.3 连网2.2.4 SSH远程连接开发板 2.3 体验 AI 应用样例 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 大家好&#xff0c;我是狮子呀&…

医疗级微型导轨:保障医疗行业手术安全!

微型直线导轨能成为一种专为医疗行业设备运用的高精度线性运动设备&#xff0c;在现代医疗领域&#xff0c;精准的位置控制和平稳的运动对于确保医疗设备的高效性能至关重要。那么&#xff0c;医疗行业对微型导轨有哪些要求呢&#xff1f; 1、精度&#xff1a;在手术过程中&…

C++客户端Qt开发——开发环境

一、QT开发环境 1.安装三个部分 ①C编译器&#xff08;gcc&#xff0c;cl.exe……) ②QT SDK SDK-->软件开发工具包 比如&#xff0c;windows版本QT SDK里已经内置了C的编译器&#xff08;内置编译器是mingw&#xff0c;windows版本的gcc/g&#xff09; ③QT的集成开发…

Python编程中用函数还是用复杂的表达式

要不要使用复杂表达式 Perl语言的原作者Larry Wall曾经说过&#xff0c;伟大的程序员都有三个优点&#xff1a;懒惰、暴躁和自负。乍一看这三个词语没有一个是褒义词&#xff0c;但在程序员的世界里&#xff0c;这三个词有不同的意义。首先&#xff0c;懒惰会促使程序员去写一…

智慧园区规划建设解决方案PPT(40页)

智慧园区规划建设解决方案摘要 1. 园区定义与发展历程 园区&#xff0c;亦称开发区&#xff0c;是在特定产业和区域政策指导下形成的区域。它们通过提供基础设施和生产空间&#xff0c;吸引投资&#xff0c;形成技术、资本密集区&#xff0c;推动经济发展。园区发展经历了四代…

Docker 部署 ShardingSphere-Proxy 数据库中间件

文章目录 Github官网文档ShardingSphere-Proxymysql-connector-java 驱动下载conf 配置global.yamldatabase-sharding.yamldatabase-readwrite-splitting.yamldockerdocker-compose.yml Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为…

【qt】TCP客户端信息的接受和发送

当有信息时的槽函数关联 跟服务端收到信息一样,当可以读一行的时候,就从套接字读一行. 发送信息也是和服务端如出一辙,通过write(). 运行结果:

java EnumSet 介绍

EnumSet 是 Java Collections Framework 中专门为枚举类型设计的高效集合实现。与其他集合类相比,EnumSet 提供了许多优点,如高效性、类型安全和易用性。它只能包含单个枚举类型的值,并且在内部使用位向量实现,因而在空间和时间上都非常高效。 EnumSet 的特点 高效性:Enu…

Spring MVC 中的文件上传 和 文件下载

Spring MVC 中的文件上传 和 文件下载 文章目录 Spring MVC 中的文件上传 和 文件下载1. Spring MVC 中的文件上传2. Spring MVC 中的文件下载3. 总结&#xff1a;4. 最后&#xff1a; 1. Spring MVC 中的文件上传 文件上传是&#xff1a;浏览器端向服务器发送文件&#xff0c…

C 语言结构体

由于近期项目需求,需使用到大量的指针与结构体&#xff0c;为更好的完成项目&#xff0c;故对结构体与指针的内容进行回顾&#xff0c;同时撰写本博客&#xff0c;方便后续查阅。 本博客涉及的结构体知识有&#xff1a; 1.0&#xff1a;结构体的创建和使用 2.0: typedef 关…

解锁音乐密码,人工智能创作动人歌词

在音乐的神秘世界里&#xff0c;每一段旋律都像是一把等待开启的密码锁&#xff0c;隐藏着无尽的情感与故事。而如今&#xff0c;人工智能正以其独特的智慧和创造力&#xff0c;帮助我们解锁这些音乐密码&#xff0c;创作出动人的歌词。 “妙笔生词智能写歌词软件&#xff08;…

Provider(1)- 什么是AudioBufferProvider

什么是AudioBufferProvider&#xff1f; 顾名思义&#xff0c;Audio音频数据缓冲提供&#xff0c;就是提供音频数据的缓冲类&#xff0c;而且这个AudioBufferProvider派生出许多子类&#xff0c;每个子类有不同的用途&#xff0c;至关重要&#xff1b;那它在Android哪个地方使…

访问 Postman OAuth 2.0 授权的最佳实践

OAuth 2.0 代表了 web 安全协议的发展&#xff0c;便于在多个平台上进行授权服务&#xff0c;同时避免暴露用户凭据。它提供了一种安全的方式&#xff0c;让用户可以授权应用程序访问服务。 在 Postman 中开始使用 OAuth 2.0 Postman 是一个流行的API客户端&#xff0c;支持 …

亚马逊店铺注册

**步骤一&#xff1a;准备注册相关资料** 在注册之前&#xff0c;请准备以下资料&#xff1a; 1.公司营业执照照片&#xff08;清晰完整的拍照上传&#xff09; 2.法人身份证正反面照片&#xff08;清晰完整的拍照上传&#xff09; 3.双币付款信用卡&#xff08;VISA&#xff0…

[PaddlePaddle飞桨] PaddleSpeech-自动语音识别-小模型部署

PaddleSpeech的GitHub项目地址 环境要求&#xff1a; gcc > 4.8.5 paddlepaddle < 2.5.1 python > 3.8 OS support: Linux(recommend), Windows, Mac OSXpip下载指令&#xff1a; python -m pip install paddlepaddle-gpu2.5.1 -i https://pypi.tuna.tsinghua.edu.c…

探索4D毫米波雷达和摄像头在自动驾驶中的潜力

随着自动驾驶技术的快速发展&#xff0c;关于各种传感器的必要性&#xff0c;尤其是LiDAR&#xff08;激光雷达&#xff09;与毫米波雷达结合摄像头的作用&#xff0c;激发了激烈的讨论。在这篇博客中&#xff0c;我们将探讨4D毫米波雷达和摄像头的组合是否可能成为自动驾驶车辆…