项目创建 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 
 - 配置访问路由 -> 
 
聊天布局
- 分为左右,左边显示最近聊天记录,点击最近聊天对象, 右边显示聊天对象详细信息
 

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

- 
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
 
详情显示

{
  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>
    );
  });
}
聊天操作

<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-scrollAPI 
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 () => {};
}, []);

常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
 - 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
 
- 提示下载完但解压或打开不了?
 
- 找不到素材资源介绍文章里的示例图片?
 
- 模板不会安装或需要功能定制以及二次开发?
 
                    
    
发表评论
还没有评论,快来抢沙发吧!