2025年5月24日
Qwen3 网页开发:类似微博!
初始提示词:
参考新浪微博开发一个微博网站
改进提示词:
这个页面设计的挺好的。但是好像少一些东西。比如热榜啊,关注推荐啊等。请完善一下,保持风格,重构布局和内容
右上方 “首页” 前面添加一个 home 的图标,”热门“前面添加一个火热的图片,“我的”改成用户头像,这些图标和文字要对齐。点击用户头像之后,还有相关的下拉菜单。比如设置,我的微博,我的收藏,我的点赞。热门话题添加编号。1,2,3 名编号颜色火热一些,其他淡一些。
热门前面的图标改成火焰相关的图标。 用户 1 发布的内容,加一些表情包。发布按钮前面添加一个表情包选择图标。
右上方 首页,热门,我的应该是一个整体,一个选中的状态下,其他就都是没选中的状态。另外我的的弹出菜单。当点击其他所以空白区域的时候应该收起来。
最终效果:


在线预览:
最终代码:
import React, { useState, useEffect, useRef } from "react"; export default function App() { const [activeTab, setActiveTab] = useState("home"); const [posts, setPosts] = useState([ { id: 1, user: "用户1", avatar: "https://picsum.photos/200/300?random=1 ", content: "这是我的第一条微博!今天心情很好🙂。", likes: 24, comments: 8, retweets: 5, time: "2小时前", }, { id: 2, user: "用户2", avatar: "https://picsum.photos/200/300?random=2 ", content: "分享一个好看的风景图~", image: "https://picsum.photos/600/400?random=3 ", likes: 17, comments: 3, retweets: 2, time: "3小时前", }, { id: 3, user: "用户3", avatar: "https://picsum.photos/200/300?random=3 ", content: "今天学习了React,感觉很有趣!", likes: 9, comments: 1, retweets: 0, time: "5小时前", }, ]); const [newPost, setNewPost] = useState(""); const [showScrollTop, setShowScrollTop] = useState(false); const [userMenuOpen, setUserMenuOpen] = useState(false); // 用户信息 const currentUser = { name: "当前用户", avatar: "https://picsum.photos/200/300?random=4 ", }; const menuRef = useRef(null); // 用于检测点击是否发生在菜单之外 // 模拟滚动事件检测 useEffect(() => { const handleScroll = () => { if (window.scrollY > 300) { setShowScrollTop(true); } else { setShowScrollTop(false); } }; window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, []); // 点击外部区域关闭下拉菜单 useEffect(() => { const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target)) { setUserMenuOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handlePost = () => { if (newPost.trim()) { const post = { id: Date.now(), user: currentUser.name, avatar: currentUser.avatar, content: newPost, likes: 0, comments: 0, retweets: 0, time: "刚刚", }; setPosts([post, ...posts]); setNewPost(""); } }; const toggleLike = (id) => { setPosts( posts.map((post) => post.id === id ? { ...post, likes: post.likes + 1 } : post ) ); }; // 热门话题数据 const hotTopics = [ { title: "#春天来了#", views: "1.2亿" }, { title: "#AI改变生活#", views: "9800万" }, { title: "#健康饮食指南#", views: "6700万" }, { title: "#明星穿搭趋势#", views: "5400万" }, { title: "#世界杯预选赛#", views: "4300万" }, ]; // 推荐关注用户 const suggestedUsers = [ { name: "科技达人", avatar: "https://picsum.photos/200/300?random=5 " }, { name: "旅行日记", avatar: "https://picsum.photos/200/300?random=6 " }, { name: "美食博主", avatar: "https://picsum.photos/200/300?random=7 " }, ]; return ( <div className="min-h-screen bg-gray-100 text-gray-800"> {/* 头部导航 */} <header className="bg-white shadow-md fixed w-full z-10"> <div className="container mx-auto px-4 py-3 flex justify-between items-center"> <h1 className="text-2xl font-bold text-red-600">Weibo</h1> <nav className="hidden md:flex space-x-6 items-center"> <button onClick={() => { setActiveTab("home"); setUserMenuOpen(false); }} className={`flex items-center gap-1 pb-1 border-b-2 ${ activeTab === "home" ? "border-red-500 text-red-500" : "border-transparent hover:border-gray-300" }`} > <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> <span>首页</span> </button> <button onClick={() => { setActiveTab("hot"); setUserMenuOpen(false); }} className={`flex items-center gap-1 pb-1 border-b-2 ${ activeTab === "hot" ? "border-red-500 text-red-500" : "border-transparent hover:border-gray-300" }`} > <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path> </svg> <span>热门</span> </button> <div className="relative" ref={menuRef}> <button onClick={() => { setActiveTab("profile"); setUserMenuOpen(!userMenuOpen); }} className={`flex items-center gap-1 pb-1 border-b-2 ${ activeTab === "profile" ? "border-red-500 text-red-500" : "border-transparent hover:border-gray-300" }`} > <img src={currentUser.avatar} alt="头像" className="w-6 h-6 rounded-full object-cover" /> <span>我的</span> </button> {/* 下拉菜单 */} {userMenuOpen && ( <div className="absolute right-0 mt-2 w-40 bg-white rounded shadow-lg z-20"> <ul className="py-1 text-sm text-gray-700"> <li className="px-4 py-2 hover:bg-gray-100 cursor-pointer"> 设置 </li> <li className="px-4 py-2 hover:bg-gray-100 cursor-pointer"> 我的微博 </li> <li className="px-4 py-2 hover:bg-gray-100 cursor-pointer"> 我的收藏 </li> <li className="px-4 py-2 hover:bg-gray-100 cursor-pointer"> 我的点赞 </li> </ul> </div> )} </div> </nav> </div> </header> <main className="container mx-auto px-4 pt-20 pb-24 flex flex-col lg:flex-row gap-6"> {/* 主内容区 */} <section className="lg:w-2/3"> {/* 发布微博表单 */} <div className="bg-white rounded-lg shadow p-4 mb-6"> <textarea value={newPost} onChange={(e) => setNewPost(e.target.value)} placeholder="有什么新鲜事想告诉大家?" className="w-full p-3 border border-gray-300 rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-red-300" rows="3" /> <div className="flex justify-between items-center mt-2"> <button className="p-1 text-gray-500 hover:text-red-500 transition-colors" title="选择表情" > <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <circle cx="12" cy="12" r="10"></circle> <path d="M8 14s1.5 2 4 2 4-2 4-2"></path> <line x1="9" y1="9" x2="9.01" y2="9"></line> <line x1="15" y1="9" x2="15.01" y2="9"></line> </svg> </button> <button onClick={handlePost} disabled={!newPost.trim()} className={`flex items-center gap-1 px-4 py-2 rounded-lg font-medium transition-colors ${ newPost.trim() ? "bg-red-500 text-white hover:bg-red-600" : "bg-gray-200 text-gray-500 cursor-not-allowed" }`} > <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <circle cx="12" cy="12" r="10"></circle> <path d="M8 14s1.5 2 4 2 4-2 4-2"></path> <line x1="9" y1="9" x2="9.01" y2="9"></line> <line x1="15" y1="9" x2="15.01" y2="9"></line> </svg> <span>发布</span> </button> </div> </div> {/* 动态列表 */} <div className="space-y-6"> {posts.map((post) => ( <div key={post.id} className="bg-white rounded-lg shadow overflow-hidden transition-transform hover:scale-[1.01]" > <div className="p-4 flex"> <img src={post.avatar} alt={post.user} className="w-12 h-12 rounded-full object-cover mr-3" /> <div className="flex-1"> <div className="flex justify-between items-start"> <div> <h3 className="font-semibold">{post.user}</h3> <span className="text-sm text-gray-500">{post.time}</span> </div> <button className="text-gray-400 hover:text-gray-600"> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <circle cx="12" cy="12" r="1"></circle> <circle cx="19" cy="12" r="1"></circle> <circle cx="5" cy="12" r="1"></circle> </svg> </button> </div> <p className="mt-2 whitespace-pre-line">{post.content}</p> {post.image && ( <img src={post.image} alt="动态图片" className="mt-3 rounded-lg max-h-96 w-full object-cover" /> )} <div className="mt-3 flex justify-between text-gray-500 text-sm"> <button onClick={() => toggleLike(post.id)} className="hover:text-red-500 flex items-center gap-1" > <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path> </svg> <span>{post.likes}</span> </button> <button className="hover:text-blue-500 flex items-center gap-1"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path> </svg> <span>{post.comments}</span> </button> <button className="hover:text-green-500 flex items-center gap-1"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M17 1l4 4-4 4"></path> <path d="M3 11V9a4 4 0 0 1 4-4h14"></path> <path d="M7 23l-4-4 4-4"></path> <path d="M21 13v2a4 4 0 0 1-4 4H3"></path> </svg> <span>{post.retweets}</span> </button> </div> </div> </div> </div> ))} </div> </section> {/* 右侧边栏:热榜 + 推荐关注 */} <aside className="lg:w-1/3 space-y-6"> {/* 热榜模块 */} <div className="bg-white rounded-lg shadow p-4"> <h2 className="text-lg font-bold mb-3">热门话题</h2> <ul className="space-y-2"> {hotTopics.map((topic, index) => ( <li key={index} className="flex justify-between items-center p-2 hover:bg-gray-100 rounded cursor-pointer" > <div className="flex items-center gap-2"> <span className={`text-xs font-bold w-5 text-center ${ index < 3 ? "text-red-500" : "text-gray-400" }`} > {index + 1} </span> <span className="font-medium">{topic.title}</span> </div> <span className="text-sm text-gray-500">{topic.views}</span> </li> ))} </ul> </div> {/* 推荐关注 */} <div className="bg-white rounded-lg shadow p-4"> <h2 className="text-lg font-bold mb-3">推荐关注</h2> <ul className="space-y-3"> {suggestedUsers.map((user, index) => ( <li key={index} className="flex items-center gap-3"> <img src={user.avatar} alt={user.name} className="w-10 h-10 rounded-full object-cover" /> <div> <h4 className="font-medium">{user.name}</h4> <button className="text-xs bg-red-100 text-red-600 px-2 py-1 rounded hover:bg-red-200"> 关注 </button> </div> </li> ))} </ul> </div> </aside> </main> {/* 底部导航(移动端) */} <nav className="md:hidden fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 flex justify-around py-3"> <button onClick={() => { setActiveTab("home"); setUserMenuOpen(false); }} className={`flex flex-col items-center ${ activeTab === "home" ? "text-red-500" : "text-gray-500" }`} > <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9z"></path> <polyline points="9 22 9 12 15 12 15 22"></polyline> </svg> <span className="text-xs mt-1">首页</span> </button> <button onClick={() => { setActiveTab("hot"); setUserMenuOpen(false); }} className={`flex flex-col items-center ${ activeTab === "hot" ? "text-red-500" : "text-gray-500" }`} > <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"></path> </svg> <span className="text-xs mt-1">热门</span> </button> <button onClick={() => { setActiveTab("profile"); setUserMenuOpen(!userMenuOpen); }} className={`flex flex-col items-center ${ activeTab === "profile" ? "text-red-500" : "text-gray-500" }`} > <img src={currentUser.avatar} alt="头像" className="w-6 h-6 rounded-full object-cover" /> <span className="text-xs mt-1">我的</span> </button> </nav> {/* 返回顶部按钮 */} {showScrollTop && ( <button onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })} className="fixed bottom-20 right-4 bg-red-500 text-white p-3 rounded-full shadow-lg hover:bg-red-600 transition-colors" > <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <polyline points="18 15 12 9 6 15"></polyline> </svg> </button> )} </div> ); }