ant design pro 使用融云IM实现聊天功能
介绍:前台使用vue开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能。效果如图(PC+移动):一、申请融云账号(token、appKey)建议先看教程:sdk使用介绍过一遍教程,接下来开始写二、引入融云IM如图://下载RongIMLib.js存放于assets中//在需要使用的页面引入require('../../assets/RongI...
·
介绍:前台使用vue+vant开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能。
效果如图(PC+移动):
一、申请融云账号(token、appKey)
建议先看教程:sdk使用介绍
过一遍教程,接下来开始写
二、引入融云IM
如图:
//下载RongIMLib.js存放于assets中
//在需要使用的页面引入
require('../../assets/RongIMLib')
三、可以正常使用RongIMLib其自带方法了
IM.js 全代码
//本打算一步步讲解如何设置,算了,我怕分开讲解容易混乱,终极大招,放代码吧
import React, { PureComponent, Fragment } from 'react';
import moment from 'moment';
import { connect } from 'dva';
import { Card, Table, Tabs, Form, Col, Row, Input, Select, Button, DatePicker ,Modal ,Divider ,Menu ,Icon} from 'antd';
import FooterToolbar from '../../components/FooterToolbar';
import PageHeaderLayout from '../../layouts/PageHeaderLayout';
import style from '../IM/IM.less';
import IMnews from '../IM/IMnews'
import { img, defaultPageSize } from '../../utils/utils';
require('../../assets/RongIMLib') //------------------------------------------重要
@connect(({ im, loading }) => ({
im,
loading: loading.models.im,
submitting: loading.effects['form/submitAdvancedForm'],
}))
@Form.create()
export default class IM extends PureComponent {
state = {
imArr:[ //左侧消息列表,还没做
{id:1,name:'刘斩仙',img:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1935453034,301169670&fm=27&gp=0.jpg'},
{id:2,name:'小仙女',img:'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3740781792,4182858516&fm=26&gp=0.jpg'},
{id:3,name:'大仙女',img:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2050311723,4151208839&fm=26&gp=0.jpg'},
{id:4,name:'猪八戒',img:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1139707926,53397985&fm=26&gp=0.jpg'},
{id:5,name:'王诛魔',img:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3559635026,3836534592&fm=26&gp=0.jpg'},
],
text:[ //模拟数据
// {
// type:1,
// css:'right',
// txt:'111',
// date:'16:40',
// headImg:'https://img.52z.com/upload/news/image/20171120/20171120080335_21404.jpg'
// },
// {
// type:1,
// css:'left',
// txt:'111',
// headImg:'https://img.52z.com/upload/news/image/20171120/20171120080335_21404.jpg'
// },
]
};
componentDidMount() {
console.log(RongIMLib)
//------------------------------------------重要
RongIMLib.RongIMClient.init('4z3hl4ovrt'); //appkey
this.beforeIm()
this.nowIm()
//实现自动滚动到底部,亲测可用 -----------------------------------------重要
let list = document.getElementById('news')
list.scrollTop = list.scrollHeight
//全屏请尝试-> document.documentElement..scrollTop = list.scrollHeight
}
componentDidUpdate(nextProps, nextState) {
//实现自动滚动到底部,亲测可用-----------------------------------------重要
let list = document.getElementById('news')
list.scrollTop = list.scrollHeight
//全屏请尝试-> document.documentElement..scrollTop = list.scrollHeight
}
handleClick = (e)=>{
console.log('我点击了',e.key)
}
handleSubmit = (e) => {
e.preventDefault();
let that = this
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('form: ', values);
that.send(that,values.say)
}
});
}
beforeIm=()=>{
let that = this
const { text } = that.state
// 连接状态监听器
RongIMClient.setConnectionStatusListener({
onChanged: function (status) {
// status 标识当前连接状态
switch (status) {
case RongIMLib.ConnectionStatus.CONNECTED:
console.log('链接成功');
break;
case RongIMLib.ConnectionStatus.CONNECTING:
console.log('正在链接');
break;
case RongIMLib.ConnectionStatus.DISCONNECTED:
console.log('断开连接');
break;
case RongIMLib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT:
console.log('其他设备登录');
break;
case RongIMLib.ConnectionStatus.DOMAIN_INCORRECT:
console.log('域名不正确');
break;
case RongIMLib.ConnectionStatus.NETWORK_UNAVAILABLE:
console.log('网络不可用');
break;
}
}
});
// 消息监听器
RongIMClient.setOnReceiveMessageListener({
// 接收到的消息
onReceived: function (message) {
// 判断消息类型
switch(message.messageType){
case RongIMClient.MessageType.TextMessage:
// message.content.content => 文字内容
console.log('8080',message,message.content.content)
let say ={ //数据格式,我自定义的消息组件所需参数,下面会发代码
type:1,
css:'left',
txt:message.content.content,
date:'',
headImg:message.content.extra
}
let arr = text;
arr.push(say)
//接收消息,追加消息的逻辑
//-----------------------------------------重要 坑!!!setState不能跟新视图,必须使用dispatch强制更新
that.props.dispatch({
type: 'im/setIM',
payload: arr,
})
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
}
}
});
}
nowIm=()=>{
//--------你自己的token-------token登陆后台返回的数据,包含token,id等聊天连接所需参数,存下来就好--------------------------重要
var token = JSON.parse(localStorage.getItem('userInfo')).IMUser.token//"WzrthC5f4UfuiI7dIwCQ5fwtGfqCdobpowIZkcQnj8PQOQuAJb/nIi1ayzGFwJguvbQZxbJH3x0=";
RongIMClient.connect(token, {
onSuccess: function(userId) {
console.log('Connect successfully. ' + userId);
},
onTokenIncorrect: function() {
console.log('token 无效');
},
onError: function(errorCode){
var info = '';
switch (errorCode) {
case RongIMLib.ErrorCode.TIMEOUT:
info = '超时';
break;
case RongIMLib.ConnectionState.UNACCEPTABLE_PAROTOCOL_VERSION:
info = '不可接受的协议版本';
break;
case RongIMLib.ConnectionState.IDENTIFIER_REJECTED:
info = 'appkey不正确';
break;
case RongIMLib.ConnectionState.SERVER_UNAVAILABLE:
info = '服务器不可用';
break;
}
console.log(info);
}
});
}
send=(that ,val)=>{
const { im: { data }, loading } = that.props;
const { text } = that.state;
let msg = new RongIMLib.TextMessage({ content: val, extra: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1551871533063&di=f62aefed291e0e3801a4ba6842fd8e85&imgtype=0&src=http%3A%2F%2Fwww.lovehhy.net%2Flib%2Fimg%2F5481858%2F854088_5481858.jpg' });
let conversationType = RongIMLib.ConversationType.PRIVATE; // 单聊, 其他会话选择相应的消息类型即可
let targetId = '1'//JSON.parse(localStorage.getItem('userInfo')).IMUser.assistantId; // 目标 Id
RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
onSuccess: function (message) {
// message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
console.log('Send successfully',message,message.content.content);
let say = {
type:1,
css:'right',
txt:message.content.content,
headImg:'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1551871533063&di=f62aefed291e0e3801a4ba6842fd8e85&imgtype=0&src=http%3A%2F%2Fwww.lovehhy.net%2Flib%2Fimg%2F5481858%2F854088_5481858.jpg'
}
let arr = data;
arr.push(say)
//发送完,追加消息的逻辑
//-----------------------------------------重要 坑!!!setState不能跟新视图,必须使用dispatch强制更新
that.props.dispatch({
type: 'im/setIM',
payload: arr,
})
that.props.form.resetFields()
// let list = document.getElementById('news')
// list.scrollTop = list.scrollHeight
},
onError: function (errorCode, message) {
let 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);
}
});
}
render() {
const { im: { }, loading } = this.props;
const { imArr ,text } = this.state
const { getFieldDecorator, getFieldValue } = this.props.form;
console.log(text)
return (
<PageHeaderLayout>
<Card className={style.box}>
<Menu //只显示,功能没做
onClick={this.handleClick}
style={{ width: 256 }}
defaultSelectedKeys={['1']}
mode="inline"
>
{imArr.map((item)=><Menu.Item key={item.id}><img src={item.img} className={style.icon}/>{item.name}</Menu.Item>)}
</Menu>
<Card bordered={false} style={{'flex':1}}>
<div className={style.content}>
<IMnews></IMnews> //--------------------------------重要,自定义消息组件
<Form onSubmit={this.handleSubmit} className={style.bottom}>
<Form.Item>
{getFieldDecorator('say', {
rules: [{ required: true, message: '不能为空' }],
})(
<Input.TextArea onPressEnter={this.handleSubmit} style={{'resize':'none'}}/>
)}
</Form.Item>
<Button type="primary" htmlType="submit" style={{float:'right',margin:'-10px 0 0 0'}} disabled={false}>发送</Button>
</Form>
</div>
</Card>
</Card>
</PageHeaderLayout>
);
}
}
im.js 全代码
import { } from '../services/api';
import { message} from 'antd';
export default {
namespace: 'im',
state: {
data:[]
},
effects: {
},
reducers: {
setIM(state, action) {
console.log(state, action)
return {
...state,
data: action.payload,
};
},
},
};
IM.less 全代码
@import "~antd/lib/style/themes/default.less";
.box {
:global{
.ant-card-body{
display: flex;
}
}
.content{
min-height: 500px;
// overflow-y:auto;
width: 100%;
// position:relative;
}
}
.icon{
width: 30px;height: 30px;border-radius: 2px;margin-right: 10px;
}
.bottom{
position: absolute;
bottom: 0;
left:0;
width: 100%;
}
IMnews.js 全代码 ----自定义消息组件(只写了文本,自己根据需求完善,红包,文章等,未写)
import React, { PureComponent, Fragment } from 'react';
import moment from 'moment';
import { connect } from 'dva';
import { Card, Table, Tabs, Form, Col, Row, Input, Select, Button, DatePicker ,Modal ,Divider ,Menu ,Icon} from 'antd';
import FooterToolbar from '../../components/FooterToolbar';
import PageHeaderLayout from '../../layouts/PageHeaderLayout';
import style from '../IM/IMnews.less';
import { img, defaultPageSize } from '../../utils/utils';
@connect(({ im, loading }) => ({
im,
loading: loading.models.im,
submitting: loading.effects['form/submitAdvancedForm'],
}))
@Form.create()
export default class IMnews extends PureComponent {
state = {
};
handleClick = (e)=>{
console.log('我点击了',e.key)
}
render() {
const { im: { data }, loading } = this.props;
const { imArr } = this.state
const { getFieldDecorator, getFieldValue } = this.props.form;
return (
<div className={style.news} id='news'>
{
data?(data.map((item,i)=>{
return (
<span key={i}>
<div hidden={item.type == 1&&item.css == 'left'?false:true}>
<div className={style.time}>{item.date}</div>
<div className={style.left}>
<img src={item.headImg} className={style.head}/>
<div className={style.frame}>
{item.txt}
</div>
</div>
</div>
<div hidden={item.type == 1&&item.css == 'right'?false:true}>
<div className={style.time}>{item.date}</div>
<div className={style.right}>
<div className={style.frameRight}>
{item.txt}
</div>
<img src={item.headImg} className={style.head}/>
</div>
</div>
</span>
)
})):''
}
</div>
);
}
}
IMnews.less 全代码
@import "~antd/lib/style/themes/default.less";
.news{
height: 400px;
overflow-y:auto;
width: 100%;
position:relative;
}
.head {
width: 36px;
height: 36px;
border-radius:5px;
}
.frame,.frameRight{
background: #eee;
border-radius:5px;
line-height: 24px;
font-size: 16px;
text-align: left;
color: #333;
padding: 10px;
box-sizing: border-box;
width: 250px;
margin-left: 15px;
position: relative;
}
.frameRight{background: #9eea6a;margin-right: 15px;}
.frame::before {
display: block;
content: '';
width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */
height: 0px;
border: 10px solid #eee;
border-bottom-color: transparent; /* 设置透明背景色 */
border-top-color: transparent;
border-left-color: transparent;
position: absolute;left:-20px;top:10px;
}
.frameRight::before{
display: block;
content: '';
width: 0px; /* 宽高设置为0,很重要,否则达不到效果 */
height: 0px;
border: 10px solid #aeea6a;
border-bottom-color: transparent; /* 设置透明背景色 */
border-top-color: transparent;
border-right-color: transparent;
position: absolute;right:-20px;top:10px;
}
.time{
color: #a1a1a1; font-size: 12px;line-height: 40px;padding-left: 10px;text-align: center;
}
.left{
box-sizing: border-box;
padding: 0 20px;
display: flex;
margin-bottom: 10px;
}
.right{
box-sizing: border-box;
padding: 0 20px;
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
完了,okk
结尾:所有代码了,不能发git,有好多公司信息,只能这样,各位老哥,将就一下
游戏篇:最近没怎么完,中午和同事吃鸡,落地成盒,完事儿。
更多推荐
已为社区贡献14条内容
所有评论(0)