从零开始用react + tailwindcss + express + mongodb实现一个聊天步伐(九) ...

打印 上一主题 下一主题

主题 943|帖子 943|积分 2829

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1.后端

 起首在models下创建 message.model.js

import mongoose from "mongoose";
const messageSchema = new mongoose.Schema(
    {  
        // 发送人Id
        senderId: {
            type: mongoose.Schema.Types.ObjectId, //在 Mongoose 模型中定义 _id 字段
            required: true,
            ref: "User"
        },
        // 接收人
        receiverId: {
            type: mongoose.Schema.Types.ObjectId, //在 Mongoose 模型中定义 _id 字段
            required: true,
            ref: "User"
        },
        // 文本
        text: {
            type: String
        },
        // 图片
        image: {
            type: String
        }
    },
    {timestamps: true});

const Message = mongoose.model('Message', messageSchema);
完善message.controller.js中 getMessages 和  sendMessages 方法
  1. import User from '../models/user.model.js';
  2. import Message from '../models/message.model.js';
  3. import cloudinary from '../lib/cloudinary.js';
  4. export const getUsersForSidebar = async (req, res) => {
  5.     try {
  6.         // 获取当前登录用户的id
  7.         const loggedInUserId = req.user._id;
  8.         // 过滤用户 所有不等于当前用户id 的用户 .select 查询时排除password
  9.         const filteredUsers = await User.find({ _id: { $ne: loggedInUserId } }).select("-password");
  10.         res.status(200).json(filteredUsers);
  11.     } catch (error) {
  12.         console.log("error in getSidebarUsers: ");
  13.         res.status(500).json({ message: error.message });
  14.     }
  15. }
  16. // 点击左侧用户时,获取该用户与当前用户之间的聊天记录
  17. export const getMessages = async (req, res) => {
  18.     try {
  19.         const {id: userToChatId} = req.params;
  20.         // 发送人id 是当前用户id
  21.         const myId = req.user._id;
  22.         
  23.         // 获取当前用户与点击的用户之间的聊天记录
  24.         const messages = await Message.find({
  25.            $or: [
  26.              {senderId: myId, receiverId: userToChatId},
  27.              {senderId: userToChatId, receiverId: myId}
  28.            ]
  29.         })
  30.         res.status(200).json(messages);
  31.     } catch (error) {
  32.         console.log("error in getMessages controller: ");
  33.         res.status(500).json({ message: error.message });
  34.     }
  35. }
  36. // 发送消息
  37. export const sendMessage = async (req, res) => {
  38.     try {   
  39.         const {text,image} = req.body;
  40.         const {id: receiverId} = req.params;
  41.         const senderId = req.user._id;
  42.         let imageUrl;
  43.         if(image) {
  44.             // 上传base64图片到cloudinary
  45.             const uploadResonse = await cloudinary.uploader.upload(image)      
  46.             imageUrl = uploadResonse.secure_url;
  47.         }
  48.         // 构建消息对象
  49.         const newMessage = new Message({
  50.             senderId,
  51.             receiverId,
  52.             text,
  53.             image: imageUrl
  54.         })
  55.         await newMessage.save();
  56.         res.status(201).json(newMessage);
  57.     } catch (error) {
  58.         console.log("error in sendMessage controller: ");
  59.         res.status(500).json({ message: error.message });
  60.     }
  61. }
复制代码
2.前端

在components 下 新建一个ChatHeader.jsx
  1. import { useChatStore } from "../store/useChatStore";
  2. import { useAuthStore } from "../store/useAuthStore";
  3. import { X } from "lucide-react";
  4. const ChatHeader = () => {
  5.     const {selectedUser,setSelectedUser} = useChatStore();
  6.   return (
  7.     <div className="p-2.5 border-b border-base-300">
  8.        <div className="flex items-center justify-between">
  9.           <div className="flex items-center">
  10.             {/* 头像 */}
  11.             <div className="avatar">
  12.                 <div className="size-10 rounded-full relative">
  13.                     <img src={selectedUser.profilePic || "https://picsum.photos/200"} alt={selectedUser.userName} />
  14.                 </div>
  15.             </div>
  16.             {/* 用户信息 */}
  17.             <div>
  18.                 <h3 className="font-medium">{selectedUser.userName}</h3>
  19.                
  20.             </div>
  21.           </div>
  22.           {/* 关闭按钮 */}
  23.           <button onClick={()=>setSelectedUser(null)}>
  24.             <X/>
  25.           </button>
  26.        </div>
  27.     </div>
  28.   )
  29. }
  30. export default ChatHeader
复制代码
然后再ChatBox.jsx 引入
 
  1. import {useEffect} from "react"
  2. import { useChatStore } from "../store/useChatStore"
  3. import { useAuthStore } from "../store/useAuthStore"
  4. import {formatMessageTime} from "@/lib/util"
  5. import ChatHeader from "./ChatHeader"
  6. const ChatBox = () => {
  7.   const {messages, getMessages, isMessagesLoading, selectedUser} = useChatStore()
  8.   const {authUser} = useAuthStore()
  9.   useEffect(()=>{
  10.     getMessages(selectedUser._id)
  11.   },[selectedUser._id, getMessages])
  12.   if(isMessagesLoading) return <div>Loading...</div>
  13.   return (
  14.     <div className="flex-1 flex flex-col overflow-auto">
  15.       {/* 聊天框头部 */}
  16.       <ChatHeader/>
  17.       
  18.       {/* 聊天消息 */}
  19.       <div className="flex-1 overflow-auto p-4 space-y-4">
  20.           {messages.map((message)=> (
  21.              <div
  22.               key={message._id}
  23.               // 消息的发送者id和当前用户id一致,则显示在右侧,否则显示在左侧
  24.               className={`chat ${message.senderId===authUser._id ? 'chat-end' : 'chat-start'}`}
  25.              >
  26.               <div className="chat-image avatar">
  27.                  <div className="size-10 rounded-full border">
  28.                     <img
  29.                       src={message.senderId === authUser._id ? authUser.profilePic || 'http://via.placeholder.com/150' : selectedUser.profilePic}
  30.                       alt=""
  31.                     />
  32.                  </div>
  33.               </div>
  34.               <div className="chat-header mb-1">
  35.                  <time className="text-xs opacity-50 ml-1">{formatMessageTime(message.createdAt)}</time>
  36.               </div>
  37.               {/* flex-col 图片和文字上下排列 */}
  38.               <div className="chat-bubble flex flex-col">
  39.                  {message.image && (
  40.                    <img
  41.                     src={message.image}
  42.                     alt=""
  43.                     className="sm:max-w-[200px] rounded-md mb-2"
  44.                    />
  45.                  )}
  46.                  {message.text && <p>{message.text}</p>}
  47.               </div>
  48.              </div>
  49.           ))}
  50.       </div>
  51.       {/* 消息输入 */}
  52.       
  53.     </div>
  54.   )
  55. }
  56. export default ChatBox
复制代码
完善useChatStore.js
import {create} from "zustand";

import toast from "react-hot-toast"
import axiosInstance from "../lib/axios";

export const useChatStore = create((set, get) => ({
    messages: [],
    users: [],
    selectedUser: null,
    isUserLoading:false,
    isMessageLoading:false,
     // 选择一个接洽人
     setSelectedUser: (user) => {
          set({selectedUser: user});
     },
    getUsers: async () => {
        set({isUserLoading: true});
       try {
            const res = await axiosInstance.get("/messages/users");
            set({users: res.data});
       } catch{
            toast.error("Error while fetching users");
       } finally{
            set({isUserLoading: false});
       }
    },
    getMessages: async (userId) => {
        set({isMessageLoading: true});
        try {
            const res = await axiosInstance.get(`/messages/${userId}`);
            set({messages: res.data});
        } catch {
            toast.error("Error while fetching messages");
        } finally {
            set({isMessageLoading: false});
        }
    },
    sendMessages: async (messageData) => {
        const {selectedUser, messages} = get();
        try {
            const res = await axiosInstance.post(`/messages/send/${selectedUser._id}`, messageData);
            set({messages: [...messages, res.data]});
        } catch(error) {
            toast.error("Error while sending message:" + error.message);
        }
    }
}))
效果如下

好这篇就到这 下一篇 实现聊天功能 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表