1.聊天框
首先我们完善前端的消息输入框
components下面新建MessageInput组件
import { useState,useRef } from "react"
import {X,Image,Send} from "lucide-react"
import { useChatStore } from "../store/useChatStore"
import toast from "react-hot-toast"
const MessageInput = () => {
const [text, setText] = useState("")
const [imagePreview, setImagePreview] = useState(null) // 预览的图片
const fileInputRef = useRef(null) // 文件输入框
const {sendMessages} = useChatStore(); // 发送消息
const handleImageChange =(e) => {
const file = e.target.files[0]
if(!file.type.startsWith("image/")) {
toast.error("请选择图片文件")
return
}
const reader = new FileReader()
reader.onload = () => {
setImagePreview(reader.result)
}
reader.readAsDataURL(file)
}
const removeImage = () => {
setImagePreview(null)
if(fileInputRef.current) fileInputRef.current.value = ""
}
const handleSendMessage = async(e) => {
e.preventDefault()
if(!text.trim() && !imagePreview) return
try {
await sendMessages({text:text.trim(),image:imagePreview})
toast.success("发送消息成功")
setText("")
setImagePreview(null)
if(fileInputRef.current) fileInputRef.current.value = ""
} catch (error) {
console.log(error)
toast.error("发送消息失败:",error.message)
}
}
return (
<div className="p-4 w-full">
{imagePreview && (
<div className="mb-3 flex items-center gap-2">
<div className="relative">
<img
src={imagePreview}
alt="Preview"
className="size-20 object-cover rounded-lg border border-zinc-700"
/>
<button
onClick={removeImage}
className="absolute -top-1.5 -right-1.5 size-5 rounded-full bg-base-300
flex items-center justify-center"
type="button"
>
<X className="size-3"/>
</button>
</div>
</div>
)}
{/* 消息输入 */}
<form onSubmit={handleSendMessage} className="flex items-center gap-2">
<div className="flex-1 flex gap-2">
<input
type="text"
className="w-full input input-bordered rounded-lg input-sm sm:input-md"
placeholder="输入消息"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<input
type="file"
accept="image/*"
className="hidden"
ref={fileInputRef}
onChange={handleImageChange}
/>
<button
type="button"
className={`hidden sm:flex btn btn-circle
${imagePreview?"text-emerald-500":"text-zinc-400"}`}
onClick={() => fileInputRef.current.click()}
>
<Image size={20} />
</button>
</div>
<button
type="submit"
className="btn btn-sm btn-circle"
disabled={!imagePreview && !text.trim()}
>
<Send size={22}/>
</button>
</form>
</div>
)
}
export default MessageInput
聊天的气泡我们参考daisyUi的 chat-start chat-end 效果如下
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"
import MessageInput from "./MessageInput"
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>
{/* 消息输入 */}
<MessageInput/>
</div>
)
}
export default ChatBox
这是我们在左侧发送消息 右侧用户就能收到消息了 并且自己发送的消息在右侧 收到的消息在左侧 正是用了 chat-start chat-end 这2个class
我们的判断逻辑是
// 消息的发送者id和当前用户id一致,则显示在右侧,否则显示在左侧
className={`chat ${message.senderId===authUser._id ? 'chat-end' : 'chat-start'}`}
思考一个问题 目前我们发送消息给对方 只有刷新页面才能收到新的消息 这是不符合要求的 所以我们引入socket.io 实现实时 的 收发消息 功能。
下篇继续。。。