feat: 精简首页 - 删除作品区域及多余社交链接
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+3
-252
@@ -1,176 +1,8 @@
|
|||||||
import { useRef, useState, useCallback, useEffect } from "react";
|
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { ArrowUpRight, FileText } from "lucide-react";
|
import { FileText } from "lucide-react";
|
||||||
import Navbar from "@/components/Navbar";
|
import Navbar from "@/components/Navbar";
|
||||||
|
|
||||||
const projects = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "ServerQA 自动化测试平台",
|
|
||||||
desc: "服务器领域的自动化测试框架,支持接口与性能测试",
|
|
||||||
tags: ["Python", "Pytest", "Locust"],
|
|
||||||
image: "/images/project-1.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "PerfMon 性能监控中心",
|
|
||||||
desc: "服务器性能实时监控与历史趋势分析仪表盘",
|
|
||||||
tags: ["Grafana", "Prometheus", "Go"],
|
|
||||||
image: "/images/project-2.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: "VibeCI 测试流水线",
|
|
||||||
desc: "基于 Vibe Coding 理念的测试持续集成工具",
|
|
||||||
tags: ["Node.js", "Docker", "GitHub Actions"],
|
|
||||||
image: "/images/project-3.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: "FaultSim 故障模拟器",
|
|
||||||
desc: "分布式系统的混沌工程测试与故障注入工具",
|
|
||||||
tags: ["Python", "Kubernetes", "gRPC"],
|
|
||||||
image: "/images/project-4.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
title: "LogLens 日志分析工具",
|
|
||||||
desc: "大规模日志的智能聚合、搜索与异常检测平台",
|
|
||||||
tags: ["ELK", "React", "ClickHouse"],
|
|
||||||
image: "/images/project-5.jpg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
title: "TestHub 用例管理平台",
|
|
||||||
desc: "测试用例的编写、评审、执行与覆盖率追踪系统",
|
|
||||||
tags: ["Next.js", "PostgreSQL", "tRPC"],
|
|
||||||
image: "/images/project-6.jpg",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function WorkGrid() {
|
|
||||||
const containerRef = useRef<HTMLUListElement>(null);
|
|
||||||
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
|
|
||||||
const [mouseX, setMouseX] = useState(0);
|
|
||||||
const [mouseY, setMouseY] = useState(0);
|
|
||||||
const [containerX, setContainerX] = useState(200);
|
|
||||||
const [containerY, setContainerY] = useState(200);
|
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
|
||||||
const rafRef = useRef<number | null>(null);
|
|
||||||
|
|
||||||
const calculateDistance = useCallback(
|
|
||||||
(node: HTMLLIElement) => {
|
|
||||||
if (!containerX || !containerY) return 0;
|
|
||||||
const rect = node.getBoundingClientRect();
|
|
||||||
const distance = Math.sqrt(
|
|
||||||
Math.pow(rect.left + rect.width / 2 - containerX, 2) +
|
|
||||||
Math.pow(rect.top + rect.height / 2 - containerY, 2)
|
|
||||||
);
|
|
||||||
return Math.round(distance * 100) / 100;
|
|
||||||
},
|
|
||||||
[containerX, containerY]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseMove = useCallback(
|
|
||||||
(e: React.MouseEvent) => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
|
||||||
setMouseX(e.clientX - rect.left);
|
|
||||||
setMouseY(e.clientY - rect.top);
|
|
||||||
|
|
||||||
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
||||||
rafRef.current = requestAnimationFrame(() => {
|
|
||||||
itemRefs.current.forEach((node) => {
|
|
||||||
if (node) {
|
|
||||||
const dist = calculateDistance(node);
|
|
||||||
node.style.setProperty("--box-distance", `${dist}px`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[calculateDistance]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(() => {
|
|
||||||
if (!containerRef.current) return;
|
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
|
||||||
setContainerX(rect.width / 2);
|
|
||||||
setContainerY(rect.height / 2);
|
|
||||||
setIsHovering(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback(() => {
|
|
||||||
setIsHovering(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (rafRef.current) cancelAnimationFrame(rafRef.current);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ul
|
|
||||||
ref={containerRef}
|
|
||||||
className="work-grid"
|
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
onMouseEnter={handleMouseEnter}
|
|
||||||
onMouseLeave={handleMouseLeave}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"--x": `${mouseX}px`,
|
|
||||||
"--y": `${mouseY}px`,
|
|
||||||
"--is-hovering": isHovering ? 1 : 0,
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{projects.map((project, index) => (
|
|
||||||
<li
|
|
||||||
key={project.id}
|
|
||||||
ref={(el) => { itemRefs.current[index] = el; }}
|
|
||||||
className="grid__item"
|
|
||||||
style={{
|
|
||||||
opacity: 0,
|
|
||||||
animation: `fadeInItem 0.6s cubic-bezier(0.19, 1, 0.22, 1) ${index * 0.1}s forwards`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="image-reveal-container" style={{ background: "#f7f6f3" }}>
|
|
||||||
<div className="aspect-[3/2] overflow-hidden">
|
|
||||||
<img
|
|
||||||
src={project.image}
|
|
||||||
alt={project.title}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="p-5">
|
|
||||||
<h3 className="text-[#141414] font-semibold text-lg mb-1">{project.title}</h3>
|
|
||||||
<p className="text-[#5a5a5a] text-sm leading-relaxed mb-3">{project.desc}</p>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{project.tags.map((tag) => (
|
|
||||||
<span
|
|
||||||
key={tag}
|
|
||||||
className="text-xs px-2.5 py-1 rounded-full bg-[#e8e6e1] text-[#5a5a5a] font-medium"
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="item__content-overlay" />
|
|
||||||
<div className="reveal-frame top-left" style={{ top: 0, left: 0 }} />
|
|
||||||
<div className="reveal-frame top-right" style={{ top: 0, right: 0 }} />
|
|
||||||
<div className="reveal-frame right-top" style={{ top: 0, right: 0 }} />
|
|
||||||
<div className="reveal-frame right-bottom" style={{ bottom: 0, right: 0 }} />
|
|
||||||
<div className="reveal-frame bottom-right" style={{ bottom: 0, right: 0 }} />
|
|
||||||
<div className="reveal-frame bottom-left" style={{ bottom: 0, left: 0 }} />
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen" style={{ background: "#e8e6e1" }}>
|
<div className="min-h-screen" style={{ background: "#e8e6e1" }}>
|
||||||
@@ -224,14 +56,6 @@ export default function Home() {
|
|||||||
transition={{ duration: 0.8, delay: 0.3, ease: [0.19, 1, 0.22, 1] }}
|
transition={{ duration: 0.8, delay: 0.3, ease: [0.19, 1, 0.22, 1] }}
|
||||||
className="flex flex-wrap gap-4"
|
className="flex flex-wrap gap-4"
|
||||||
>
|
>
|
||||||
<a
|
|
||||||
href="#works"
|
|
||||||
className="inline-flex items-center gap-2 px-7 py-3.5 rounded-2xl text-white font-medium text-base transition-all duration-300 hover:shadow-lg hover:translate-y-[-2px]"
|
|
||||||
style={{ background: "#4a6cf7" }}
|
|
||||||
>
|
|
||||||
查看作品
|
|
||||||
<ArrowUpRight size={18} />
|
|
||||||
</a>
|
|
||||||
<Link
|
<Link
|
||||||
to="/blog"
|
to="/blog"
|
||||||
className="inline-flex items-center gap-2 px-7 py-3.5 rounded-2xl font-medium text-base transition-all duration-300 hover:shadow-md hover:translate-y-[-2px]"
|
className="inline-flex items-center gap-2 px-7 py-3.5 rounded-2xl font-medium text-base transition-all duration-300 hover:shadow-md hover:translate-y-[-2px]"
|
||||||
@@ -255,7 +79,7 @@ export default function Home() {
|
|||||||
<div className="flex flex-wrap items-center gap-3">
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
{/* Bilibili */}
|
{/* Bilibili */}
|
||||||
<a
|
<a
|
||||||
href="https://space.bilibili.com/"
|
href="https://space.bilibili.com/297925716?spm_id_from=333.1007.0.0"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
||||||
@@ -270,7 +94,7 @@ export default function Home() {
|
|||||||
|
|
||||||
{/* CSDN */}
|
{/* CSDN */}
|
||||||
<a
|
<a
|
||||||
href="https://blog.csdn.net/"
|
href="https://blog.csdn.net/weixin_45839854?spm=1000.2115.3001.5343"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
||||||
@@ -283,80 +107,13 @@ export default function Home() {
|
|||||||
<span>CSDN</span>
|
<span>CSDN</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{/* GitHub */}
|
|
||||||
<a
|
|
||||||
href="https://github.com/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
|
||||||
style={{ background: "#f7f6f3", color: "#5a5a5a" }}
|
|
||||||
title="GitHub"
|
|
||||||
>
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
|
||||||
</svg>
|
|
||||||
<span>GitHub</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{/* Juejin 掘金 */}
|
|
||||||
<a
|
|
||||||
href="https://juejin.cn/user/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
|
||||||
style={{ background: "#f7f6f3", color: "#5a5a5a" }}
|
|
||||||
title="稀土掘金"
|
|
||||||
>
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M4.2 4h15.6c.6 0 1 .4 1 1v1.3c0 .6-.4 1-1 1H4.2c-.6 0-1-.4-1-1V5c0-.6.4-1 1-1zm0 4.7h10.4c.6 0 1 .4 1 1v1.3c0 .6-.4 1-1 1H4.2c-.6 0-1-.4-1-1v-1.3c0-.6.4-1 1-1zm0 4.6h15.6c.6 0 1 .4 1 1v1.3c0 .6-.4 1-1 1H4.2c-.6 0-1-.4-1-1v-1.3c0-.6.4-1 1-1zm0 4.7h10.4c.6 0 1 .4 1 1V20c0 .6-.4 1-1 1H4.2c-.6 0-1-.4-1-1v-1c0-.6.4-1 1-1z"/>
|
|
||||||
</svg>
|
|
||||||
<span>稀土掘金</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{/* Zhihu 知乎 */}
|
|
||||||
<a
|
|
||||||
href="https://www.zhihu.com/people/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="group flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 hover:shadow-md"
|
|
||||||
style={{ background: "#f7f6f3", color: "#5a5a5a" }}
|
|
||||||
title="知乎"
|
|
||||||
>
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
||||||
<path d="M5.721 0C2.251 0 0 2.25 0 5.719V18.28C0 21.751 2.252 24 5.721 24h12.56C21.751 24 24 21.75 24 18.281V5.72C24 2.249 21.75 0 18.281 0zm1.964 4.078c-.271.73-.5 1.434-.68 2.11h4.587c.545-.006.445 1.168.445 1.168H6.283a58.104 58.104 0 0 1-.037 3.32h3.342c.524.006.59 1.22.59 1.22H6.183c.04.8.228 2.025.455 3.07.215.977.475 1.74.717 2.087.24.346.414.31.6.27.184-.04.56-.346.84-1.028.28-.683.397-1.565.397-1.565h1.34s-.06.966-.373 2.032c-.315 1.066-.93 2.468-2.37 2.91-1.44.442-2.453-.224-2.916-.85-.462-.626-.78-2.026-.97-3.18-.19-1.155-.255-2.36-.255-3.165H2.26c.006-.55.12-1.224.12-1.22h1.88V6.186H3.47c-.12 0-.164-.82-.164-1.108h4.38zm8.137.004h1.56l2.14 8.39 2.17-8.39h1.49l-3.16 10.868h-1.15l-3.05-10.868zm-2.76 0h1.39v10.868h-1.39V4.082z"/>
|
|
||||||
</svg>
|
|
||||||
<span>知乎</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Works Section */}
|
|
||||||
<section id="works" className="py-24 md:py-32" style={{ background: "#e8e6e1" }}>
|
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 30 }}
|
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
|
||||||
viewport={{ once: true, margin: "-100px" }}
|
|
||||||
transition={{ duration: 0.8, ease: [0.19, 1, 0.22, 1] }}
|
|
||||||
className="mb-16"
|
|
||||||
>
|
|
||||||
<h2
|
|
||||||
className="font-semibold mb-4"
|
|
||||||
style={{ fontSize: "clamp(32px, 4vw, 48px)", lineHeight: 1.1, color: "#141414" }}
|
|
||||||
>
|
|
||||||
精选作品
|
|
||||||
</h2>
|
|
||||||
<p className="text-[#5a5a5a] text-lg max-w-xl">
|
|
||||||
过去几年里,我参与和主导的一些代表性项目。
|
|
||||||
</p>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<WorkGrid />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="py-12" style={{ background: "#e8e6e1" }}>
|
<footer className="py-12" style={{ background: "#e8e6e1" }}>
|
||||||
<div className="max-w-7xl mx-auto px-6 text-center">
|
<div className="max-w-7xl mx-auto px-6 text-center">
|
||||||
@@ -375,12 +132,6 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>{`
|
|
||||||
@keyframes fadeInItem {
|
|
||||||
from { opacity: 0; transform: translateY(20px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user