介绍
因为有同学问了我下融云怎么写聊天的问题,我才了解到有融云这么个东西,尝试着学了下。主要介绍了vue中使用vue-cli接入融云实现即时通信的相关资料,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...
准备
注册融云拿到 APPKEY 和 TOKEN
在写代码之前我们首先要 注册融云,注册融云后拿到 appkey 和融云提供的 token
拿到 appkey 和 token 后记得保存下来
安装 Vue CLI
可以使用下列任一命令安装这个新的包:
$ npm install -g @vue/cli
# OR
$ yarn global add @vue/cli
创建一个项目
运行以下命令来创建一个新项目:
$ vue create hello-world
# OR
$ vue ui // UI 界面创建项目
你会被提示选取一个 prese 因为我们这个项目用到了 VUEX 所以我们选择手动选择特性来选取需要的特性来避免后续的的麻烦
除了默认选项 我们需要选择 Router
和 Vuex
等项目创建完毕后大概是这个样子
开始编写我们的代码
在这之间还是希望大家去过一遍官方文档后来看我的文章
引入融云 Web SDK
首先我们进入根目录的 public
目录下的 index.html
这个位置引入融云 SDK
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>im</title>
</head>
<body>
<noscript>
<strong>We're sorry but im doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script src="https://cdn.ronghub.com/RongIMLib-2.5.0.min.js"></script>
<!-- built files will be auto injected -->
</body>
</html>
我们先在根目录 src
下创建几个文件 名字随意大家可以自行创建
- 创建一个js文件
utils.js
- 创建
scss
目录 在此目录创建一个utils.scss
(这个文件并不重要只是为了自适应) - 在
scr
目录下创建init.vue
- 在
src
目录下创建RongCloud.vue
- 在
src
目录下创建Message.vue
开始撸代码
utils.js
此文件是对融云的初始化操作
export const init = (params, addPromptInfo) => {
var appkey = params.appkey
var token = params.token
RongIMClient.init(appkey)
RongIMClient.setConnectionStatusListener({
onChanged: function (status) {
switch (status) {
case RongIMLib.ConnectionStatus['CONNECTED']:
case 0:
addPromptInfo('连接成功')
break
case RongIMLib.ConnectionStatus['CONNECTING']:
case 1:
addPromptInfo('正在连接中')
break
case RongIMLib.ConnectionStatus['DISCONNECTED']:
case 2:
addPromptInfo('当前用户主动断开链接')
break
case RongIMLib.ConnectionStatus['NETWORK_UNAVAILABLE']:
case 3:
addPromptInfo('网络不可用')
break
case RongIMLib.ConnectionStatus['CONNECTION_CLOSED']:
case 4:
addPromptInfo('未知原因,连接关闭')
break
case RongIMLib.ConnectionStatus['KICKED_OFFLINE_BY_OTHER_CLIENT']:
case 6:
addPromptInfo('用户账户在其他设备登录,本机会被踢掉线')
break
case RongIMLib.ConnectionStatus['DOMAIN_INCORRECT']:
case 12:
addPromptInfo('当前运行域名错误,请检查安全域名配置')
break
}
}
})
// 消息监听器
RongIMClient.setOnReceiveMessageListener({
// 接收到的消息
onReceived: function (message) {
// 判断消息类型
switch (message.messageType) {
case RongIMClient.MessageType.TextMessage:
// message.content.content => 文字内容
addPromptInfo('新消息 ' + message.targetId + ':' + JSON.stringify(message))
break
case RongIMClient.MessageType.VoiceMessage:
// message.content.content => 格式为 AMR 的音频 base64
break
case RongIMClient.MessageType.ImageMessage:
// message.content.content => 图片缩略图 base64
// message.content.imageUri => 原图 URL
break
case RongIMClient.MessageType.LocationMessage:
// message.content.latiude => 纬度
// message.content.longitude => 经度
// message.content.content => 位置图片 base64
break
case RongIMClient.MessageType.RichContentMessage:
// message.content.content => 文本消息内容
// message.content.imageUri => 图片 base64
// message.content.url => 原图 URL
break
case RongIMClient.MessageType.InformationNotificationMessage:
// do something
break
case RongIMClient.MessageType.ContactNotificationMessage:
// do something
break
case RongIMClient.MessageType.ProfileNotificationMessage:
// do something
break
case RongIMClient.MessageType.CommandNotificationMessage:
// do something
break
case RongIMClient.MessageType.CommandMessage:
// do something
break
case RongIMClient.MessageType.UnknownMessage:
// do something
break
default:
// do something
}
}
})
RongIMClient.connect(token, {
onSuccess: function (userId) {
addPromptInfo('连接成功,用户id:' + userId, userId)
},
onTokenIncorrect: function () {
addPromptInfo('token无效')
},
onError: function (errorCode) {
switch (errorCode) {
case RongIMLib.ErrorCode.TIMEOUT:
addPromptInfo('超时')
//链接超时进行重新的链接start
var callback = {
onSuccess: function (userId) {
console.log("Reconnect successfully." + userId);
},
onTokenIncorrect: function () {
console.log('token无效');
},
onError: function (errorCode) {
console.log(errorcode);
}
};
var config = {
// 默认 false, true 启用自动重连,启用则为必选参数
auto: true,
// 重试频率 [100, 1000, 3000, 6000, 10000, 18000] 单位为毫秒,可选
url: 'cdn.ronghub.com/RongIMLib-2.5.0.min.js',
rate: [100, 1000, 3000, 6000, 10000]
};
RongIMClient.reconnect(callback, config);
//链接超时进行重新链接end
break;
case RongIMLib.ErrorCode.UNKNOWN_ERROR:
addPromptInfo('未知错误')
break;
case RongIMLib.ErrorCode.UNACCEPTABLE_PaROTOCOL_VERSION:
addPromptInfo('不可接受的协议版本')
break;
case RongIMLib.ErrorCode.IDENTIFIER_REJECTED:
console.log('ddd')
addPromptInfo('appkey不正确')
break;
case RongIMLib.ErrorCode.SERVER_UNAVAILABLE:
addPromptInfo('服务器不可用')
break;
}
addPromptInfo(errorCode)
}
}, null)
}
utils.scss
这个文件很简单
@function vw ($px) {
@return $px / 750px * 100vw;
}
init.vue
通过用户输入的信息来进行初始化连接
<template>
<div class="init">
<van-nav-bar title="连接融云" />
<van-cell-group>
<van-field v-model="appkey"
required
label="appkey"
placeholder="请输入您的 appkey" />
<van-field v-model="token"
label="token"
placeholder="请输入您的 token"
required />
<van-field v-model="targetId"
label="targetId"
placeholder="请输入 targetId"
required />
</van-cell-group>
<van-button class="init-button"
type="info"
@click="initRongCloud">初始化连接</van-button>
<div class="rong-show-box">
<p v-for="data in showDatas"
v-bind:key="data">
{{data}}
</p>
</div>
</div>
</template>
<script>
import { init } from '@/utils.js'
export default {
data () {
return {
appkey: '', // 这是我们之前保存的 appkey *重要
token: '', // token 可以多次生成 之前也有介绍过
targetId: '', // 你要给谁发送消息 目标ID
showDatas: [], // 初始化信息
}
},
methods: {
addPromptInfo (prompt, userId = null) {
const _this = this
const avatarList = [
'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4100987808,2324741924&fm=26&gp=0.jpg',
'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2988245209,2476612762&fm=26&gp=0.jpg',
'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=4259300811,497831842&fm=26&gp=0.jpg',
'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3684587473,1286660191&fm=26&gp=0.jpg',
'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2884107401,3797902000&fm=26&gp=0.jpg'
]
// 真实环境是通过登录 后台接口返回的 token 拿到的用户信息 我在这为为了模拟 所以给初始化后的用户随机生成一个头像
const avatar = avatarList[Math.floor(Math.random() * (3 + 1))]
_this.showDatas.push(prompt)
const timer = setInterval(() => {
if (userId) {
clearInterval(timer) // 路由跳转后销毁定时器
_this.$store.commit('SET_MEMBER', { // 保存用户信息
userId: userId,
avatar: avatar
})
_this.$store.commit('SET_TARGETID', _this.targetId) // 保存目标ID
_this.$router.push({ name: 'RongCloud' })
}
}, 500)
},
initRongCloud () {
var appkey = this.appkey
var token = this.token
if (!appkey || !token) {
alert('appkey 和 token 不能为空')
} else {
// 这个init 是我们之前撸的 `utils.js`
init({
appkey: appkey,
token: token
}, this.addPromptInfo)
}
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.init-button {
position: fixed !important;
bottom: vw(30px);
left: 50%;
transform: translateX(-50%);
}
.rong-show-box {
margin-top: vw(100px);
text-align: center;
}
</style>
RongCloud.vue
发送消息
<template>
<div id='rongcloud'>
<van-nav-bar title="融云聊天"
fixed
left-text="返回"
left-arrow
@click-left="onClickLeft" />
<div class="wrapper">
<Message v-for="(item, index) in answer"
:key="index"
:data='item' />
</div>
<div class="send-message">
<van-field v-model="say"
class="message-textarea"
type="textarea"
placeholder="请输入..." />
<van-button class="send-button"
type="info"
size="small"
@click="sendMessage">发送</van-button>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import Message from './Message'
export default {
components: {
Message
},
data () {
return {
say: '' // 消息
}
},
created () {
this.$nextTick(() => {
const list = document.getElementById('rongcloud')
document.documentElement.scrollTop = list.scrollHeight
//如不行,请尝试-> list.scrollTop = list.scrollHeight
})
},
watch: {
answer () {
this.$nextTick(() => {
const list = document.getElementById('rongcloud')
document.documentElement.scrollTop = list.scrollHeight
//如不行,请尝试-> list.scrollTop = list.scrollHeight
})
}
},
computed: {
...mapState({
answer: 'answer', // 消息列表
memberInfo: 'memberInfo', // 用户信息
targetId: 'targetId' // 目标ID
})
},
methods: {
onClickLeft () {
this.$router.go(-1)
},
sendMessage () {
const _this = this
var msg = new RongIMLib.TextMessage({ content: _this.say, extra: _this.memberInfo.avatar });
var conversationType = RongIMLib.ConversationType.PRIVATE // 单聊, 其他会话选择相应的消息类型即可
var targetId = this.targetId // 目标 Id
RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
onSuccess: function (message) {
// message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
const say = {
css: 'right',
txt: message.content.content,
headImg: _this.memberInfo.avatar
}
_this.answer.push(say)
_this.say = ''
},
onError: function (errorCode, message) {
var info = ''
switch (errorCode) {
case RongIMLib.ErrorCode.TIMEOUT:
info = '超时'
break
case RongIMLib.ErrorCode.UNKNOWN:
info = '未知错误'
break
case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
info = '在黑名单中,无法向对方发送消息'
break
case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
info = '不在讨论组中'
break
case RongIMLib.ErrorCode.NOT_IN_GROUP:
info = '不在群组中'
break
case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
info = '不在聊天室中'
break
}
console.log('发送失败: ' + info + errorCode)
}
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.wrapper {
padding-top: vw(92px);
padding-bottom: vw(200px);
}
.send-message {
width: 100vw;
height: vw(200px);
position: fixed !important;
bottom: 0;
left: 0;
.message-textarea {
height: 100%;
}
.send-button {
position: fixed;
right: vw(30px);
bottom: vw(30px);
}
}
</style>
Message.vue
显示消息 逻辑比较简单就不讲了
<template>
<div>
<div v-if="data.css === 'left'">
<div class="message left">
<van-image round
fit="cover"
width="2rem"
height="2rem"
:src="data.headImg" />
<span>{{data.txt}}</span>
</div>
</div>
<div v-if="data.css === 'right'">
<div class="message right">
<span>{{data.txt}}</span>
<van-image round
fit="cover"
width="2rem"
height="2rem"
:src="data.headImg" />
</div>
</div>
</div>
</template>
<script>
export default {
props: ['data']
}
</script>
<style lang="scss" scoped>
@import "~@/scss/utils";
.message {
display: flex;
align-items: center;
padding: vw(10px);
}
.left {
justify-content: flex-start;
span {
margin-left: vw(20px);
}
}
.right {
justify-content: flex-end;
span {
margin-right: vw(20px);
}
}
</style>
store.js
此demo的一些状态管理数据
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
memberInfo: undefined, // 用户信息
targetId: undefined, // 目标ID
answer: [] // 消息列表
}
const mutations = {
SET_MEMBER (state, memberInfo) {
state.memberInfo = memberInfo
},
SET_TARGETID (state, targetId) {
state.targetId = targetId
},
SET_ANSWER (state, playload) {
let say = {
css: 'left', // css 样式
txt: playload.content, // 文本内容
headImg: playload.extra // 头像
}
state.answer.push(say)
},
};
export default new Vuex.Store({
state,
mutations,
actions
})
最后一步我们需要在 utils.js
文件中引入store 保存发送的消息到 answer
import store from './store'
export const init = (params, addPromptInfo) => {
...
// 消息监听器
RongIMClient.setOnReceiveMessageListener({
// 接收到的消息
onReceived: function (message) {
// 判断消息类型
switch (message.messageType) {
case RongIMClient.MessageType.TextMessage:
// message.content.content => 文字内容
store.commit('SET_ANSWER', message.content)
...
}
}
})
...
}
结尾
本 demo 是给大家实现一个基本思路,只是实现了一个一对一聊天的功能。如需更多功能,请自行深入了解,谢谢。