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>
);
}