最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React 构建聊天界面

    正文概述 掘金(江景)   2021-03-11   758

    项目创建 React 体验开箱即用

    React 构建聊天界面

    实现步骤 - 目录定义

    基于 antd.design-pro 脚手架

    • layouts 添加聊天界面布局
    • pages 目录添加聊天界面

    文件结构

    ├── src
    │   ├── layouts
    │   │   ├── layouts
    │   │   ├── ChatLayout.jsx  # 聊天布局
    │   │   ├── ChatLayout.less # 聊天布局样式
    │   ├── pages
    │   │   ├── Im
    │   │   │   ├── chat
    │   │   │   │   ├── index.jsx # 聊天界面
    │   │   │   │   ├── idnex.less # 聊天界面样式
    │   │   │   ├── index.jsx # 聊天初始化界面
    

    配置访问路由 config/routes.js

    {
      path: '/im',
      name: 'im',
      icon: 'wechat', // 显示图标
      routes: [
        {
          path: '/im', // 访问路由地址
          component: '../layouts/ChatLayout', // 聊天界面使用聊天布局
          routes: [
            {
              path: '/im', // 初始化界面
              component: './Im',
            },
            {
              path: '/im/chat', // 具体聊天界面显示
              name: 'chat',
              component: './Im/chat', // 对应组件
            },
          ],
        },
      ],
    },
    

    添加国际化 locales/zh-CN/menu.js

      'menu.im': '即时通讯',
      'menu.chat': '聊天',
    

    实现步骤 - Mock 数据准备

    使用 dva 状态数据管理

    models 目录下创建 chat.js

    const ChatModel = {
      namespace: "chat",
    
      state: {
        // 当前聊天对象
        chatUserInfo: {},
        // 输入聊天信息
        content: "",
        // 聊天详情
        messageList: [
          {
            msgId: "7751131831315698898",
            sendId: "775113183131074580",
            content: "你吃饭了吗?",
          },
          {
            msgId: "7751133655565656565",
            sendId: "801901013480247300",
            content: "黄河之水天上来。",
          },
        ],
        // 最近聊天列表
        chatRecordList: [
          {
            msgId: "775113183131069599",
            sendId: "801901013480247300",
            receiveId: "775113183131074580",
            type: "1",
            chatType: "1",
            sendTime: "2021-03-09",
            content: "黄河之水天上来。",
          },
        ],
      },
    
      effects: {},
    
      reducers: {
        // 当前和那个聊天
        chatCurrentUser(state, { payload: { chatUserInfo } }) {
          return {
            ...state,
            chatUserInfo,
          };
        },
    
        // 刷新聊天列表
        refreshChatList(state, { payload: { messageList } }) {
          return {
            ...state,
            messageList: messageList,
          };
        },
    
        // 输入消息改变时
        chatInputMessageChange(state, { payload: { message } }) {
          return {
            ...state,
            content: message,
          };
        },
      },
      subscriptions: {
        setup({ history }) {
          history.listen(({ pathname, search }) => {});
        },
      },
    };
    export default ChatModel;
    

    实现步骤 - 布局

    ChatLayout 代码

    import { Link } from "umi";
    import { connect } from "dva";
    
    import ChatRecordItem from "@/components/ChatRecordItem";
    
    import styles from "./ChatLayout.less";
    
    const ChatLayout = ({ dispatch, children, chat }) => {
      const { chatUserInfo, chatRecordList } = chat;
    
      // 点击切换当前聊天对象
      // model/chat 中定义 `chat/chatCurrentUser`
      const onChangeChatCurrentUser = (item) => {
        dispatch({
          type: "chat/chatCurrentUser",
          payload: {
            chatUserInfo: item,
          },
        });
      };
      return (
        <div className={styles["chat-layout-container"]}>
          <div className={styles["chat-message-list"]}>
            {chatRecordList.map((item) => {
              return (
                <Link to="/im/chat" key={item.msgId}>
                  <ChatRecordItem
                    {...item}
                    // 选中聊天对象
                    selected={item.sendId === chatUserInfo.sendId}
                    onClick={() => onChangeChatCurrentUser(item)}
                  />
                </Link>
              );
            })}
          </div>
          <div className={styles["chat-message-content"]}>{children}</div>
        </div>
      );
    };
    
    export default connect(({ chat }) => ({
      chat,
    }))(ChatLayout);
    

    ChatLayout 代码说明

    没有选择聊天对象显示
    import { Card, Empty } from "antd";
    
    export default () => (
      <Card>
        <Empty description="请选择聊天对象" />
      </Card>
    );
    
    • antd.design-pro Layout 定义
    • 单独创建聊天布局
    • route.js 中使用
      • 配置访问路由 -> config/routes.js
    聊天布局
    • 分为左右,左边显示最近聊天记录,点击最近聊天对象, 右边显示聊天对象详细信息

    React 构建聊天界面

    ChatRecordItem 为封装的组件,显示每一天最近聊天记录

    React 构建聊天界面

    • initUserList 为 mock 数据,存放 mock 用户信息(用户名称、头像)

      export const initUserList = [
        {
          userId: "801901013480247300",
          userName: "李白",
          avatar:
            "https://gitee.com/shizidada/moose-resource/raw/master/blog/default-avatar.png",
        },
      ];
      
    • 后期对接 SpringBoot 集成 netty-socketio

    import { Avatar, Divider } from "antd";
    import { initUserList } from "@/mock/userList";
    
    import cls from "classnames";
    
    import styles from "./index.less";
    
    const ChatRecordItem = ({ sendId, sendTime, content, selected, onClick }) => {
      const [userInfo] = initUserList.filter((item) => item.userId === sendId);
      if (!userInfo) return null;
      let selectedClassName = styles["chat-record-item-selected"];
      return (
        <div>
          <div
            className={cls(styles["chat-record-item"], {
              [selectedClassName]: selected,
            })}
            onClick={onClick}
          >
            <div className={styles["chat-user-avatar"]}>
              <Avatar src={userInfo.avatar} />
            </div>
            <div className={styles["chat-user-info"]}>
              <div className={styles.top}>
                <p className={styles["chat-user-name"]}>
                  {userInfo.userName || ""}
                </p>
                <span className={styles.time}>{sendTime}</span>
              </div>
              <div className={styles["chat-message-detail-item"]}>
                <p className={styles["chat-message-detail"]}> {content}</p>
              </div>
            </div>
          </div>
          <Divider style={{ margin: 0 }} />
        </div>
      );
    };
    
    export default ChatRecordItem;
    

    聊天

    选择聊天对象显示

    • Im -> chat -> index.jsx
    详情显示

    React 构建聊天界面

    {
      messageList.map((item, index) => {
        return item.sendId !== userId ? (
          <div className={styles["chat-item"]} key={item.msgId || index}>
            <div className={styles["chat-receiver"]}>
              {/* receiver */}
              <div className={styles["avatar-wrap"]}>
                <div className={styles.avatar}>
                  <Avatar
                    size="large"
                    style={{ backgroundColor: "#005EFF", verticalAlign: "middle" }}
                  >
                    {toUserName}
                  </Avatar>
                </div>
              </div>
              <div className={styles.content}>{item.content}</div>
            </div>
          </div>
        ) : (
          <div className={styles["chat-item"]} key={item.messageId || index}>
            <div className={styles["chat-sender"]}>
              {/* sender */}
              <div className={styles.content}>{item.content}</div>
              <div className={styles["avatar-wrap"]}>
                <div className={styles.avatar}>
                  <Avatar
                    size="large"
                    style={{ backgroundColor: "#005EFF", verticalAlign: "middle" }}
                  >
                    {userName}
                  </Avatar>
                </div>
              </div>
            </div>
          </div>
        );
      });
    }
    
    聊天操作

    React 构建聊天界面

    <div className={styles["chat-input-area"]}>
      <Row>
        <Input.TextArea
          placeholder="请输入消息"
          autoSize={{ minRows: 4, maxRows: 5 }}
          value={content}
          onChange={(e) =>
            dispatch({
              type: "chat/chatInputMessageChange",
              payload: {
                message: e.target.value,
              },
            })
          }
        />
      </Row>
      <Row type="flex" justify="end" style={{ marginTop: 10, marginRight: 10 }}>
        <Col>
          <Button type="primary" onClick={onSendMessage} disabled={!content}>
            发送
          </Button>
          {/* for test */}
          {/* <Button type="primary" onClick={onMessageScroll}>滚动</Button> */}
        </Col>
      </Row>
    </div>
    

    发送消息

    • 必须选择聊天对象
    • 内容必须输入
    • 触发 ChatModel 定义 reducers;添加消息,重新刷新当前视图
    const onSendMessage = () => {
      if (!receiveId) {
        message.error("请选择聊天对象");
        return;
      }
    
      if (!content) {
        return;
      }
    
      let messageTemplate = {
        type: "MS:TEXT",
        chatType: "CT:SINGLE",
        content,
        sendId: userId,
        receiveId: receiveId,
      };
      const temp = messageList.concat();
      temp.push(messageTemplate);
      dispatch({
        type: "chat/refreshChatList",
        payload: {
          messageList: temp,
        },
      });
      dispatch({
        type: "chat/chatInputMessageChange",
        payload: {
          message: null,
        },
      });
    };
    

    超过消息容器,自动滚动至底部

    • 可以使用原生 js 实现
    • 当前使用 react-scroll 模块实现

    使用 react-scroll

    • npm install react-scroll --save

    • 添加 react-scroll 代码

    import { Element } from "react-scroll";
    
    • 在遍历完消息后面添加
    <Element name="bottomElement"></Element>
    

    React 构建聊天界面

    • 调用 react-scroll API
    import { scroller } from "react-scroll";
    
    // scrollId 为 Element name 定义
    scroller.scrollTo(`scrollId`, {
      duration: 800,
      delay: 0,
      smooth: true,
      containerId: `containerId`,
      offset: 50,
    });
    

    封装 react-scroll API

    • utils/scroller.js
    import { scroller } from "react-scroll";
    
    export const scrollToBottom = (scrollId, containerId) => {
      scroller.scrollTo(scrollId, {
        duration: 800,
        delay: 0,
        smooth: true,
        containerId: containerId,
        offset: 50,
      });
    };
    

    使用

    • 选中聊天对象调用 scrollToBottom
    • 在发送消息完成之后调用 scrollToBottom
    const onMessageScroll = () => {
      scrollToBottom("bottomElement", "chatItems");
    };
    
    // 发送消息
    ...
     dispatch({
        type: 'chat/chatInputMessageChange',
        payload: {
          message: null,
        },
      });
      onMessageScroll(); // 调用 scrollToBottom
    };
    ....
    
    // 进入页面
    useEffect(() => {
      // 没有选中聊天对象,返回 聊天初始化界面
    if (!receiveId) {
        history.replace({ pathname: '/im' });
        return;
      }
      onMessageScroll();
      return () => {};
    }, []);
    
    

    React 构建聊天界面


    起源地下载网 » React 构建聊天界面

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元