马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
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 方法
- import User from '../models/user.model.js';
- import Message from '../models/message.model.js';
- import cloudinary from '../lib/cloudinary.js';
- export const getUsersForSidebar = async (req, res) => {
- try {
- // 获取当前登录用户的id
- const loggedInUserId = req.user._id;
- // 过滤用户 所有不等于当前用户id 的用户 .select 查询时排除password
- const filteredUsers = await User.find({ _id: { $ne: loggedInUserId } }).select("-password");
- res.status(200).json(filteredUsers);
- } catch (error) {
- console.log("error in getSidebarUsers: ");
- res.status(500).json({ message: error.message });
- }
- }
- // 点击左侧用户时,获取该用户与当前用户之间的聊天记录
- export const getMessages = async (req, res) => {
- try {
- const {id: userToChatId} = req.params;
- // 发送人id 是当前用户id
- const myId = req.user._id;
-
- // 获取当前用户与点击的用户之间的聊天记录
- const messages = await Message.find({
- $or: [
- {senderId: myId, receiverId: userToChatId},
- {senderId: userToChatId, receiverId: myId}
- ]
- })
- res.status(200).json(messages);
- } catch (error) {
- console.log("error in getMessages controller: ");
- res.status(500).json({ message: error.message });
- }
- }
- // 发送消息
- export const sendMessage = async (req, res) => {
- try {
- const {text,image} = req.body;
- const {id: receiverId} = req.params;
- const senderId = req.user._id;
- let imageUrl;
- if(image) {
- // 上传base64图片到cloudinary
- const uploadResonse = await cloudinary.uploader.upload(image)
- imageUrl = uploadResonse.secure_url;
- }
- // 构建消息对象
- const newMessage = new Message({
- senderId,
- receiverId,
- text,
- image: imageUrl
- })
- await newMessage.save();
- res.status(201).json(newMessage);
- } catch (error) {
- console.log("error in sendMessage controller: ");
- res.status(500).json({ message: error.message });
- }
- }
复制代码 2.前端
在components 下 新建一个ChatHeader.jsx
- import { useChatStore } from "../store/useChatStore";
- import { useAuthStore } from "../store/useAuthStore";
- import { X } from "lucide-react";
- const ChatHeader = () => {
- const {selectedUser,setSelectedUser} = useChatStore();
- return (
- <div className="p-2.5 border-b border-base-300">
- <div className="flex items-center justify-between">
- <div className="flex items-center">
- {/* 头像 */}
- <div className="avatar">
- <div className="size-10 rounded-full relative">
- <img src={selectedUser.profilePic || "https://picsum.photos/200"} alt={selectedUser.userName} />
- </div>
- </div>
- {/* 用户信息 */}
- <div>
- <h3 className="font-medium">{selectedUser.userName}</h3>
-
- </div>
- </div>
- {/* 关闭按钮 */}
- <button onClick={()=>setSelectedUser(null)}>
- <X/>
- </button>
- </div>
- </div>
- )
- }
- export default ChatHeader
复制代码 然后再ChatBox.jsx 引入
- import {useEffect} from "react"
- import { useChatStore } from "../store/useChatStore"
- import { useAuthStore } from "../store/useAuthStore"
- import {formatMessageTime} from "@/lib/util"
- import ChatHeader from "./ChatHeader"
- const ChatBox = () => {
- const {messages, getMessages, isMessagesLoading, selectedUser} = useChatStore()
- const {authUser} = useAuthStore()
- useEffect(()=>{
- getMessages(selectedUser._id)
- },[selectedUser._id, getMessages])
- if(isMessagesLoading) return <div>Loading...</div>
- return (
- <div className="flex-1 flex flex-col overflow-auto">
- {/* 聊天框头部 */}
- <ChatHeader/>
-
- {/* 聊天消息 */}
- <div className="flex-1 overflow-auto p-4 space-y-4">
- {messages.map((message)=> (
- <div
- key={message._id}
- // 消息的发送者id和当前用户id一致,则显示在右侧,否则显示在左侧
- className={`chat ${message.senderId===authUser._id ? 'chat-end' : 'chat-start'}`}
- >
- <div className="chat-image avatar">
- <div className="size-10 rounded-full border">
- <img
- src={message.senderId === authUser._id ? authUser.profilePic || 'http://via.placeholder.com/150' : selectedUser.profilePic}
- alt=""
- />
- </div>
- </div>
- <div className="chat-header mb-1">
- <time className="text-xs opacity-50 ml-1">{formatMessageTime(message.createdAt)}</time>
- </div>
- {/* flex-col 图片和文字上下排列 */}
- <div className="chat-bubble flex flex-col">
- {message.image && (
- <img
- src={message.image}
- alt=""
- className="sm:max-w-[200px] rounded-md mb-2"
- />
- )}
- {message.text && <p>{message.text}</p>}
- </div>
- </div>
- ))}
- </div>
- {/* 消息输入 */}
-
- </div>
- )
- }
- 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企服之家,中国第一个企服评测及商务社交产业平台。 |