ant design pro 使用融云IM实现聊天功能

介绍:前台使用vue开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能。

效果如图(PC+移动):
ant design pro 使用融云IM实现聊天功能
ant design pro 使用融云IM实现聊天功能
一、申请融云账号(token、appKey)
建议先看教程:sdk使用介绍
过一遍教程,接下来开始写

二、引入融云IM
如图:
ant design pro 使用融云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,有好多公司信息,只能这样,各位老哥,将就一下
游戏篇:最近没怎么完,中午和同事吃鸡,落地成盒,完事儿。