使用LeanCloud平台的即时通讯

LeanCloud 是领先的 Serverless 云服务,为产品开发提供强有力的后端支持,旨在帮助开发者降低研发、运营维护等阶段投入的精力和成本。 LeanCloud 整合了各项服务,让开发者能够聚焦在核心业务上,为客户创造更多价值。

*即时通讯

LeanCloud地址

效果图

在这里插入图片描述

安装

npm install leancloud-realtime leancloud-realtime-plugin-typed-messages leancloud-storage --save

简单封装ChatComponent.vue

<template><div class="chat"><div class="userList"><ul><li v-for="item in userList" :class="{ active: item === adverse }" :key="item" @click="selectUser(item)">{{ item }}</li></ul></div><div class="instant-messaging"><h3>{{ adverse }}</h3><ul v-show="newMessage.length !== 0" ref="scrollContainer" id="scrollContainer" class="scroll-container"><li v-for="item in newMessage" :key="item" class="message-item":style="`justify-content: ${item.from !== creator ? 'flex-start' : 'flex-end'};`"><div v-if="item.from !== creator" class="message-content"><div class="user"><img :src="'https://img0.baidu.com/it/u=2226630510,461838410&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=521'"alt="" /></div><div class="message image" :style="'padding: 6px;'" v-if="item.content._lctype === -2"><img :src="item.content._lcfile.url" alt="" /></div><div class="message" v-if="item.content._lctype === -1">{{ item.content._lctext }}</div></div><div v-else class="message-content"><div class="message image" :style="'padding: 6px;'" v-if="item.content._lctype === -2"><img :src="item.content._lcfile.url" alt="" /></div><div class="message" v-if="item.content._lctype === -1">{{ item.content._lctext }}</div><div class="user"><img :src="'https://img1.baidu.com/it/u=3622150954,2575811681&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500'"alt="" /></div></div></li></ul><ul v-show="newMessage.length === 0"><li>暂无消息</li></ul><div class="message-operation"><!-- <el-button type="primary" @click="moreMessage">更多</el-button> --><el-upload v-model:file-list="fileList" ref="uploadRef" class="upload-demo" multiple :limit="3":auto-upload="false" :show-file-list="false" :accept="'image/*'"><el-button :icon="Picture" /></el-upload><el-input class="message-input" v-model="messageValue" @keyup.enter="sendMessage" style="width: 240px"placeholder="" /><el-button type="primary" @click="sendMessage">发送</el-button></div></div></div>
</template><script setup>
import * as IM from 'leancloud-realtime'
import AV from 'leancloud-storage'
import initPlugin from 'leancloud-realtime-plugin-typed-messages'
import { onMounted, ref, watch, onBeforeUnmount } from 'vue'
import { ElMessage } from 'element-plus'
import { Picture } from '@element-plus/icons-vue'
import axios from 'axios'const props = defineProps({// 自己的 idownId: {type: String,default: ''},// 对方的 idadverseId: {type: String,default: ''},// 聊天室名称chatsName: {type: String,default: ''},
})const { Realtime, TextMessage, Event } = IM
const { TypedMessagesPlugin, ImageMessage } = initPlugin(AV, IM)
let realtime;const newMessage = ref([])
const creator = ref('')
const adverse = ref('')
const messageValue = ref('')
const fileList = ref([])
const uploadRef = ref(null)
const scrollContainer = ref(null)// 监听文件上传
watch(() => fileList.value.length,() => {console.log(fileList.value)fileList.value.length !== 0 && sendMessage()}
)// 发送消息
const sendMessage = () => {console.log(messageValue.value === '' && fileList.value.length === 0)if (messageValue.value === '' && fileList.value.length === 0) {ElMessage({message: '不能发送空消息!',type: 'warning'})return}/*** 创建一个 IMClient 实例*/// 1、Tom 用自己的名字作为 clientId 来登录即时通讯服务realtime.createIMClient(props.ownId).then(function (own) {// 成功登录console.log('登录成功', own)// 创建与 Jerry 之间的对话own.createConversation({// tom 是一个 IMClient 实例// 指定对话的成员除了当前用户 Tom(SDK 会默认把当前用户当做对话成员)之外,还有 Jerrymembers: [props.adverseId],// 对话名称name: props.chatsName,unique: true}).then(function (conversation) {// 创建成功console.log('创建成功', conversation)// 发送一条文本消息if (messageValue.value !== '') {conversation.send(new TextMessage(messageValue.value)).then(function (message) {console.log('发送成功!')createUser_Jerry()messageValue.value = ''}).catch(console.error)}if (fileList.value.length !== 0) {var file = new AV.File('avatar.jpg', fileList.value[0].raw)file.save().then(function () {var message = new ImageMessage(file)message.setText('本地图片')message.setAttributes({ location: '武汉' })return conversation.send(message)}).then(function () {console.log('发送成功')createUser_Jerry()fileList.value = []}).catch(console.error.bind(console))}})}).catch(console.error)
}const createUser_Jerry = () => {// Jerry 登录realtime.createIMClient(props.ownId).then(function (own) {// 成功登录console.log('登录成功', own)creator.value = own.id// 确保 Jerry 已加入对话own.createConversation({members: [props.adverseId],name: props.chatsName,unique: true}).then(function (conversation) {// 创建成功console.log('创建成功', conversation)adverse.value = conversation.members.find((item) => item !== conversation.creator)// 当前用户被添加至某个对话own.on(Event.INVITED, function invitedEventHandler(payload, conversation) {console.log(payload.invitedBy, conversation.id)})// 当前用户收到了某一条消息,可以通过响应 Event.MESSAGE 这一事件来处理。own.on(Event.MESSAGE, function (message, conversation) {// newMessage.value.push(message.text)console.log('收到新消息:' + message.text, conversation)getChatRecord(conversation)})getChatRecord(conversation)})}).catch(console.error)
}let messageIterator
// 获取聊天记录
const getChatRecord = (conversation) => {// conversation//     .queryMessages({//         limit: 10, // limit 取值范围 1~100,默认 20//     })//     .then(function (messages) {//         // 最新的十条消息,按时间增序排列//         newMessage.value = messages//     })//     .catch(console.error.bind(console));// JS SDK 通过迭代器隐藏了翻页的实现细节,开发者通过不断的调用 next 方法即可获得后续数据。// 创建一个迭代器,每次获取 10 条历史消息messageIterator = conversation.createMessagesIterator({ limit: 50 })// 第一次调用 next 方法,获得前 10 条消息,还有更多消息,done 为 falsemessageIterator.next().then(function (result) {// result: {//   value: [message1, ..., message10],//   done: false,// }console.log(result)newMessage.value = result.valuescrollContainer.value.scrollTop = scrollContainer.value.scrollHeight}).catch(console.error.bind(console))
}// 更多消息
const moreMessage = () => {// 第二次调用 next 方法,获得第 11~20 条消息,还有更多消息,done 为 false// 迭代器内部会记录起始消息的数据,无需开发者显示指定messageIterator.next().then(function (result) {// result: {//   value: [message21, ..., message30],//   done: true,// }if (result.value.length === 0) {ElMessage.warning('没有更多消息了')return}//   newMessage.value = [...result.value, ...newMessage.value]newMessage.value = result.value.concat(newMessage.value)}).catch(console.error.bind(console))
}let isScrollLocked = true
// 滚动事件
const handleScroll = () => {if (!isScrollLocked && isNearTop(scrollContainer.value)) {isScrollLocked = true // 锁定滚动条位置const ulContainer = document.getElementById('scrollContainer')console.log(ulContainer.scrollHeight)setTimeout(() => {moreMessage()isScrollLocked = false}, 1000)} else {setTimeout(() => {isScrollLocked = false}, 1000);}
}/* 节流 */
const throttle = (func, delay) => {let time = nullreturn function () {let args = Array.from(arguments)if (time === null) {time = setTimeout(() => {func(...args)clearTimeout(time)time = null}, delay)}}
}// 是否到达顶部
const isNearTop = (scrollContainer) => {const threshold = 50 // 可调整的阈值,距离底部多少像素时触发加载return scrollContainer.scrollTop <= threshold
}// 延迟一段时间后再获取滚动高度
const checkAndSetScrollTop = () => {const ulContainer = document.getElementById('scrollContainer')// 确保滚动高度不为零if (ulContainer.scrollHeight !== 0) {// 将滚动条设置到最底部ulContainer.scrollTop = ulContainer.scrollHeight} else {// 如果滚动高度仍为0,再延迟几秒重新检查setTimeout(checkAndSetScrollTop, 500) // 这里的延迟时间可以根据实际情况调整}
}const userList = ref([])
// 获取会话
const getConversation = () => {axios({method: 'get',url: `/1.2/rtm/conversations?limit=10&where={"c": "${props.ownId}"}`,headers: {'X-LC-Id': 'NLblIbY4gEKenESSb7Q3vY9Y-gzGzoHsz','X-LC-Key': 'l1JvznaAgLvkGumBdiXDYQ6q,master','Content-Type': 'application/json'}}).then(function (response) {console.log(response.data);userList.value = response.data.results.map(item => {return item.m.find(v => v !== item.c)})console.log(userList.value);});
}
const emit = defineEmits(['selectUser'])
// 选择用户
const selectUser = (user) => {emit('selectUser', user)
}// 监听props.adverseId
watch(() => props.adverseId,() => {createUser_Jerry()getConversation()getUnreader()}
)// 获取未读消息
const getUnreader = () => {axios({method: 'get',url: `/1.2/rtm/clients/${props.ownId}/unread-count`,headers: {'X-LC-Id': 'NLblIbY4gEKenESSb7Q3vY9Y-gzGzoHsz','X-LC-Key': 'l1JvznaAgLvkGumBdiXDYQ6q,master','Content-Type': 'application/json'}}).then(function (response) {console.log(response.data);if (response.data.unread > 0) {ElMessage.warning('有新消息')}});
}onMounted(() => {realtime = new Realtime({appId: 'NLblIbY4gEKenESSb7Q3vY9Y-gzGzoHsz',appKey: 'pVpG3Q8DWAnWDWUquOb5cBu0',server: 'https://nlbliby4.lc-cn-n1-shared.com',// 初始化即时通讯服务时需要指定富媒体消息插件plugins: [TypedMessagesPlugin]})localStorage.setItem('debug', 'LC*')AV.init({appId: 'NLblIbY4gEKenESSb7Q3vY9Y-gzGzoHsz',appKey: 'pVpG3Q8DWAnWDWUquOb5cBu0',serverURL: 'https://nlbliby4.lc-cn-n1-shared.com'})createUser_Jerry()getConversation()getUnreader()checkAndSetScrollTop() // 延迟一段时间后再获取滚动高度scrollContainer.value.addEventListener('scroll', throttle(handleScroll, 1000))
})onBeforeUnmount(() => {scrollContainer.value.removeEventListener('scroll', handleScroll)
})
</script><style lang="scss" scoped>
* {ul,li {list-style: none;padding: 0;margin: 0;}
}.chat {display: flex;.userList {border: 1px solid #e3e3e3;width: 16%;ul {li {line-height: 36px;background: #e3e3e3;padding: 0 10px;margin-bottom: 1px;cursor: pointer;}li:hover{background: #cacaca;}.active{background: #c1c1c1;}}}.instant-messaging {flex: 1;background: #f1f1f1;padding: 20px;h3 {text-align: center;}.scroll-container {height: 580px;overflow-y: auto;&::-webkit-scrollbar {/*滚动条整体样式*/width: 10px;/*高宽分别对应横竖滚动条的尺寸*/height: 10px;}&::-webkit-scrollbar-thumb {/*滚动条里面小方块*/border-radius: 10px;background: #ccc;box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);}.message-item {display: flex;margin-bottom: 10px;.message-content {display: flex;align-items: center;.user {width: 36px;height: 36px;margin-right: 10px;border-radius: 50px;img {width: 100%;height: 100%;}}.message {line-height: 36px;background: #fff;padding: 0 6px;margin-right: 10px;display: flex;justify-content: center;}.image {max-width: 100px;height: 52px;img {width: 100%;height: 100%;}}}}}.message-operation {display: flex;justify-content: flex-end;.message-input {margin: 0 20px;}}}
}
</style>

使用组件

<template><div><ChatComponent :ownId="ownId" :adverseId="adverseId" :chatsName="chatsName" @selectUser="selectUser" /></div>
</template><script setup>
import ChatComponent from '@/components/ChatComponent.vue'
import { ref } from 'vue'// const ownId = ref('goto_w')
// const adverseId = ref('tom')
// const chatsName = ref('goto_w  & tom')// const ownId = ref('红红')
// const adverseId = ref('熊熊')
// const chatsName = ref('红红  & 熊熊')const ownId = ref('goto_w')
const adverseId = ref('M')
const chatsName = ref('goto_w & M')const selectUser = (user) => {adverseId.value = user
}</script>

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

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

相关文章

基于安卓的虫害识别软件设计--(1)模型训练与可视化

引言 简介&#xff1a;使用pytorch框架&#xff0c;从模型训练、模型部署完整地实现了一个基础的图像识别项目计算资源&#xff1a;使用的是Kaggle&#xff08;每周免费30h的GPU&#xff09; 1.创建名为“utils_1”的模块 模块中包含&#xff1a;训练和验证的加载器函数、训练…

【Python爬虫--scrapy+selenium框架】超详细的Python爬虫scrapy+selenium框架学习笔记(保姆级别的,非常详细)

六&#xff0c;selenium 想要下载PDF或者md格式的笔记请点击以下链接获取 python爬虫学习笔记点击我获取 Scrapyselenium详细学习笔记点我获取 Python超详细的学习笔记共21万字点我获取 1&#xff0c;下载配置 ## 安装&#xff1a; pip install selenium## 它与其他库不同…

【C++】C++11新特性:列表初始化、声明、新容器、右值引用、万能引用和完美转发

目录 一、列表初始化 1.1 { } 初始化 1.2 std::initializer_list 二、声明 2.1 auto 2.2 decltype 2.3 nullptr 三、新容器 四、右值引用和移动语义 4.1 左值和左值引用 4.2 右值和右值引用 4.3 左值引用与右值引用比较 4.4 右值引用使用场景和意义&#xff1a;移…

万字长文深度解析Agent反思工作流框架Reflexion下篇:ReflectionAgent workflow

在前文[LLM-Agents]万字长文深度解析Agent反思工作流框架Reflexion中篇&#xff1a;React中&#xff0c;我们详细解析了ReactAgent的工作流程&#xff0c;而本文则将在此基础上探讨反思技巧的应用。之前的文章中[LLM-Agents]反思Reflection 工作流我们已经对反思技巧进行了探讨…

多维数组操作,不要再用遍历循环foreach了!来试试数组展平的小妙招!array.flat()用法与array.flatMap() 用法及二者差异详解

目录 一、array.flat&#xff08;&#xff09;方法 1.1、array.flat&#xff08;&#xff09;的语法及使用 ①语法 ②返回值 ③用途 二、array.flatMap() 方法 2.1、array.flatMap()的语法及作用 ①语法 ②返回值 ③用途 三、array.flat&#xff08;&#xff09;与a…

CLIP 源码分析:simple_tokenizer.py

tokenizer的含义 from .clip import *引入头文件时为什么有个. 正文 import gzip import html import os from functools import lru_cacheimport ftfy import regex as re# 上面的都是头文件# 这段代码定义了一个函数 default_bpe()&#xff0c;它使用了装饰器 lru_cache()。…

一维时间序列信号的改进小波降噪方法(MATLAB R2021B)

目前国内外对于小波分析在降噪方面的方法研究中&#xff0c;主要有小波分解与重构法降噪、小波阈值降噪、小波变换模极大值法降噪等三类方法。 (1)小波分解与重构法降噪 早在1988 年&#xff0c;Mallat提出了多分辨率分析的概念&#xff0c;利用小波分析的多分辨率特性进行分…

DAQmx Connect Terminals (VI) 信号路由作用及意义

DAQmx Connect Terminals是一个LabVIEW虚拟仪器&#xff08;VI&#xff09;&#xff0c;用于配置和连接数据采集系统中的物理终端或虚拟终端。这一功能在配置复杂的数据采集&#xff08;DAQ&#xff09;系统时非常重要&#xff0c;因为它允许用户在不改变硬件连接的情况下&…

Python——Selenium快速上手+方法(一站式解决问题)

目录 前言 一、Selenium是什么 二、Python安装Selenium 1、安装Selenium第三方库 2、下载浏览器驱动 3、使用Python来打开浏览器 三、Selenium的初始化 四、Selenium获取网页元素 4.1、获取元素的实用方法 1、模糊匹配获取元素 & 联合多个样式 2、使用拉姆达表达式 3、加上…

Python零基础-下【详细】

接上篇继续&#xff1a; Python零基础-中【详细】-CSDN博客 目录 十七、网络编程 1、初识socket &#xff08;1&#xff09;socket理解 &#xff08;2&#xff09;图解socket &#xff08;3&#xff09;戏说socket &#xff08;4&#xff09;网络服务 &#xff08;5&a…

实战经验分享之移动云快速部署Stable Diffusion SDXL 1.0

本文目录 前言产品优势部署环境准备模型安装测试运行 前言 移动云是中国移动面向政府、企业和公众的新型资源服务。 客户以购买服务的方式&#xff0c;通过网络快速获取虚 拟计算机、存储、网络等基础设施服务&#xff1b;软件开发工具、运行环境、数据库等平台服务&#xff1…

【评价类模型】熵权法

1.客观赋权法&#xff1a; 熵权法是一种客观求权重的方法&#xff0c;所有客观求权重的模型中都要有以下几步&#xff1a; 1.正向化处理&#xff1a; 极小型指标&#xff1a;取值越小越好的指标&#xff0c;例如错误率、缺陷率等。 中间项指标&#xff1a;取值在某个范围内较…

[ubuntu18.04]搭建mptcp测试环境说明

MPTCP介绍 Multipath TCP — Multipath TCP -- documentation 2022 documentation 安装ubuntu18.04&#xff0c;可以使用虚拟机安装 点击安装VMware Tool 桌面会出现如下图标 双击打开VMware Tools&#xff0c;复制如下图所示的文件到Home目录 打开终端&#xff0c;切换到管…

[Linux]重定向

一、struct file内核对象 struct file是在内核中创建&#xff0c;专门用来管理被打开文件的结构体。struct file中包含了打开文件的所有属性&#xff0c;文件的操作方法集以及文件缓冲区&#xff08;无论读写&#xff0c;我们都需要先将数据加载到文件缓冲区中。&#xff09;等…

基于JSP的高校二手交易平台

开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;浏览器&#xff08;如360浏览器、谷歌浏览器、QQ浏览器等&#xff09;、MySQL数据库 系统展示 系统功能界面 用户注册与登录界面 个人中心界面 商品信息界面 摘要 本文研究了高…

为何懂行的人都在选海信Mini LED?

今年的618大促比往年来得要更早一些。纵览各电商平台的电视产品&#xff0c;能发现Mini LED电视的出镜率很高&#xff0c;成了各大品牌的主推产品。 对于什么样的Mini LED更值得买&#xff0c;各品牌都有自己的说辞。因为缺乏科学系统的选购标准&#xff0c;消费者容易在各方说…

【Qt】【模型-视图架构】代理模型示例

文章目录 1. 基本排序/过滤模型Basic Sort/Filter Model Example2. 自定义排序/过滤模型Custom Sort/Filter Model ExampleFilterLineEdit类定义及实现MySortFilterProxyModel类定义及实现 1. 基本排序/过滤模型Basic Sort/Filter Model Example 官方提供的基本排序/过滤模型示…

docker镜像体积优化攻略参考—— 筑梦之路

简单介绍 镜像的本质是镜像层和运行配置文件组成的压缩包&#xff0c;构建镜像是通过运行 Dockerfile 中的 RUN 、COPY 和 ADD 等指令生成镜像层和配置文件的过程。 和镜像体积大小有关的关键点&#xff1a; RUN、COPY 和 ADD 指令会在已有镜像层的基础上创建一个新的镜像层&…

【数据结构】详解二叉树

文章目录 1.树的结构及概念1.1树的概念1.2树的相关结构概念1.3树的表示1.4树在实际中的应用 2.二叉树的结构及概念2.1二叉树的概念2.2特殊的二叉树2.2.1满二叉树2.2.2完全二叉树 2.3 二叉树的性质2.4二叉树的存储结构2.4.1顺序结构2.4.2链表结构 1.树的结构及概念 1.1树的概念…

基于SSM的车辆租赁管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的车辆租赁管理系统1拥有两种角色 管理员&#xff1a;用户管理、用户租车、用户换车和车辆入库、添加汽车、添加客户、生成出租单、客户选车、出租单管理、查询出租单、角色权限管…