Files
zhaoguiyang.site/src/pages/projects.astro
zguiyang a208bab7ea feat(portfolio): redesign site as opportunity gateway
- add now/hire pages and update IA/navigation

- feature Elynd in projects and homepage messaging

- refresh services/blog/brand copy for AI product positioning

- reduce hydration overhead by using Astro Container

- remove Google Fonts external dependency and use local fallback stack
2026-03-13 14:01:04 +08:00

323 lines
18 KiB
Plaintext

---
import Layout from "@/layouts/Layout.astro";
import GlassHeader from "@/components/GlassHeader";
import Footer from "@/components/Footer";
import Container from "@/components/ui/Container.astro";
import { useTranslations } from "@/i18n/utils";
import type { Lang } from "@/types/i18n";
import { defaultLang } from "@/i18n/ui";
import { projects } from "@/lib/data/index";
// 使用Astro.currentLocale获取当前语言环境
const lang = Astro.currentLocale as Lang || defaultLang;
const t = useTranslations(lang);
const pageTitle = t('projects.title');
// 根据当前语言获取项目数据
const currentProjects = projects[lang as keyof typeof projects] || projects.en;
// Separate featured and grouped projects
const featuredProject = currentProjects.find(p => p.featured);
const otherProjects = currentProjects.filter(p => !p.featured);
// Group by type
const productProjects = otherProjects.filter(p => p.type === 'product');
const clientProjects = otherProjects.filter(p => p.type === 'client');
const experimentProjects = otherProjects.filter(p => p.type === 'experiment');
---
<Layout title={pageTitle}>
<GlassHeader lang={lang} client:only="react" />
<main class="min-h-screen">
<!-- Projects Hero Section -->
<section class="py-24 relative overflow-hidden">
<!-- Background gradient -->
<div class="absolute inset-0 bg-gradient-to-br from-purple-900/20 via-purple-800/10 to-purple-700/20 dark:from-purple-900/30 dark:via-purple-800/20 dark:to-purple-700/30"></div>
<Container className="relative z-10">
<div class="text-center mb-16">
<!-- Main title -->
<h1 class="text-5xl md:text-6xl font-bold mb-6 bg-gradient-to-r from-gray-900 via-purple-600 to-purple-800 dark:from-white dark:via-purple-200 dark:to-purple-300 bg-clip-text text-transparent">
{t('projects.title')}
</h1>
<!-- Description -->
<p class="text-lg text-muted-foreground max-w-3xl mx-auto mb-12 leading-relaxed">
{t('projects.slogan')} {t('projects.description')}
</p>
</div>
</Container>
</section>
<!-- Featured Project Section -->
{featuredProject && (
<section class="py-12 bg-gradient-to-br from-purple-900/10 to-blue-900/10 dark:from-purple-900/20 dark:to-blue-900/20">
<Container>
<div class="max-w-5xl mx-auto">
<div class="flex items-center gap-4 mb-8">
<span class="inline-block px-4 py-1 bg-purple-500/20 text-purple-600 dark:text-purple-400 rounded-full text-sm font-medium">
Featured
</span>
</div>
<div class="bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-2xl border border-purple-500/30 overflow-hidden">
<div class="grid lg:grid-cols-2">
<div class="h-64 lg:h-auto bg-gradient-to-br from-purple-600/20 via-blue-600/20 to-purple-600/20 flex items-center justify-center relative overflow-hidden">
<div class="text-8xl">{featuredProject.icon}</div>
</div>
<div class="p-8">
<div class="flex items-center gap-3 mb-4">
<span class="px-3 py-1 bg-purple-500/20 text-purple-600 dark:text-purple-400 rounded-full text-sm">
{t(`project.status.${featuredProject.status}`)}
</span>
<span class="px-3 py-1 bg-blue-500/20 text-blue-600 dark:text-blue-400 rounded-full text-sm">
{t(`project.type.${featuredProject.type}`)}
</span>
</div>
<h2 class="text-3xl font-bold mb-4">{featuredProject.title}</h2>
<div class="space-y-4 mb-6">
<div>
<span class="text-sm text-muted-foreground">{t('project.role')}:</span>
<p class="font-medium">{featuredProject.role}</p>
</div>
<div>
<span class="text-sm text-muted-foreground">{t('project.impact')}:</span>
<p class="font-medium">{featuredProject.impact}</p>
</div>
</div>
<div class="space-y-2 mb-6">
{featuredProject.description.map((desc) => (
<p class="text-muted-foreground text-sm">{desc}</p>
))}
</div>
<div class="flex flex-wrap gap-2 mb-6">
{featuredProject.tech.map((tech) => (
<span class="px-3 py-1 bg-gray-500/10 dark:bg-gray-400/20 text-gray-600 dark:text-gray-300 text-sm rounded-md border border-gray-500/20 dark:border-gray-400/30">{tech}</span>
))}
</div>
<div class="flex gap-4">
<a href={featuredProject.link} class="bg-purple-500 hover:bg-purple-600 text-white px-6 py-2 rounded-lg font-medium transition-colors inline-flex items-center gap-2">
{t('project.visit')}
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
{featuredProject.links?.github && (
<a href={featuredProject.links.github} target="_blank" rel="noopener noreferrer" class="border border-gray-500 text-gray-600 dark:text-gray-300 hover:bg-gray-500/10 px-6 py-2 rounded-lg font-medium transition-colors inline-flex items-center gap-2">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<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>
GitHub
</a>
)}
</div>
</div>
</div>
</div>
</div>
</Container>
</section>
)}
<!-- Projects by Type -->
<section class="py-12 relative">
<Container>
<!-- Product Projects -->
{productProjects.length > 0 && (
<>
<div class="flex items-center gap-4 mb-8">
<div class="w-10 h-10 bg-purple-500/20 rounded-lg flex items-center justify-center">
<span class="text-xl">🛍️</span>
</div>
<h2 class="text-2xl font-bold">{t('project.type.product')}</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
{productProjects.map((project) => (
<div class={`group overflow-hidden border border-${project.color}-500/20 hover:border-${project.color}-500/40 h-full flex flex-col transition-all duration-500 hover:scale-105 bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-xl relative`}>
<div class="absolute top-4 right-4 z-10 flex gap-2">
<span class={`px-3 py-1 bg-${project.color}-100 dark:bg-${project.color}-800 text-${project.color}-800 dark:text-${project.color}-200 text-xs rounded-full font-medium border border-${project.color}-200 dark:border-${project.color}-700`}>{t(`project.status.${project.status}`)}</span>
</div>
<div class={`h-48 bg-gradient-to-br ${project.image.bg} relative overflow-hidden`}>
<div class={`absolute inset-0 bg-gradient-to-br ${project.image.bg} group-hover:${project.image.hover} transition-all duration-500`}></div>
<div class="absolute bottom-4 left-4 right-4">
<div class={`bg-black/50 backdrop-blur-sm rounded px-3 py-1 text-xs font-mono ${project.image.text}`}>
{project.title}
</div>
</div>
</div>
<div class="p-6 pb-4">
<h3 class={`text-xl group-hover:text-${project.color}-400 transition-colors duration-300 flex items-center gap-2 font-bold mb-4`}>
<span class={`text-${project.color}-500`}>{project.icon}</span>
{project.title}
</h3>
</div>
<div class="px-6 flex-grow">
<div class="space-y-3">
{project.description.slice(0, 2).map((desc) => (
<div class="flex items-start gap-2 text-sm text-muted-foreground group-hover:text-foreground transition-colors duration-300">
<span class={`text-${project.color}-500 mt-1`}>•</span>
<span>{desc}</span>
</div>
))}
</div>
<div class="mt-6 mb-4 flex flex-wrap gap-2">
{project.tech.slice(0, 4).map((tech) => (
<span class="px-2 py-1 bg-gray-500/10 dark:bg-gray-400/20 text-gray-600 dark:text-gray-300 text-xs rounded-md border border-gray-500/20 dark:border-gray-400/30">{tech}</span>
))}
</div>
</div>
<div class="p-6 pt-2">
<a href={project.link} class={`inline-flex items-center text-sm font-medium text-${project.color}-500 hover:text-${project.color}-600 dark:hover:text-${project.color}-400 transition-colors duration-300`}>
{t('project.visit')}
<svg class="ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
</div>
</div>
))}
</div>
</>
)}
<!-- Client Projects -->
{clientProjects.length > 0 && (
<>
<div class="flex items-center gap-4 mb-8">
<div class="w-10 h-10 bg-blue-500/20 rounded-lg flex items-center justify-center">
<span class="text-xl">💼</span>
</div>
<h2 class="text-2xl font-bold">{t('project.type.client')}</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
{clientProjects.map((project) => (
<div class={`group overflow-hidden border border-${project.color}-500/20 hover:border-${project.color}-500/40 h-full flex flex-col transition-all duration-500 hover:scale-105 bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-xl relative`}>
<div class="absolute top-4 right-4 z-10 flex gap-2">
<span class={`px-3 py-1 bg-${project.color}-100 dark:bg-${project.color}-800 text-${project.color}-800 dark:text-${project.color}-200 text-xs rounded-full font-medium border border-${project.color}-200 dark:border-${project.color}-700`}>{t(`project.status.${project.status}`)}</span>
</div>
<div class={`h-48 bg-gradient-to-br ${project.image.bg} relative overflow-hidden`}>
<div class={`absolute inset-0 bg-gradient-to-br ${project.image.bg} group-hover:${project.image.hover} transition-all duration-500`}></div>
<div class="absolute bottom-4 left-4 right-4">
<div class={`bg-black/50 backdrop-blur-sm rounded px-3 py-1 text-xs font-mono ${project.image.text}`}>
{project.title}
</div>
</div>
</div>
<div class="p-6 pb-4">
<h3 class={`text-xl group-hover:text-${project.color}-400 transition-colors duration-300 flex items-center gap-2 font-bold mb-4`}>
<span class={`text-${project.color}-500`}>{project.icon}</span>
{project.title}
</h3>
</div>
<div class="px-6 flex-grow">
<div class="space-y-3">
{project.description.slice(0, 2).map((desc) => (
<div class="flex items-start gap-2 text-sm text-muted-foreground group-hover:text-foreground transition-colors duration-300">
<span class={`text-${project.color}-500 mt-1`}>•</span>
<span>{desc}</span>
</div>
))}
</div>
<div class="mt-6 mb-4 flex flex-wrap gap-2">
{project.tech.slice(0, 4).map((tech) => (
<span class="px-2 py-1 bg-gray-500/10 dark:bg-gray-400/20 text-gray-600 dark:text-gray-300 text-xs rounded-md border border-gray-500/20 dark:border-gray-400/30">{tech}</span>
))}
</div>
</div>
<div class="p-6 pt-2">
<a href={project.link} class={`inline-flex items-center text-sm font-medium text-${project.color}-500 hover:text-${project.color}-600 dark:hover:text-${project.color}-400 transition-colors duration-300`}>
{t('project.visit')}
<svg class="ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
</div>
</div>
))}
</div>
</>
)}
<!-- Experiment Projects -->
{experimentProjects.length > 0 && (
<>
<div class="flex items-center gap-4 mb-8">
<div class="w-10 h-10 bg-green-500/20 rounded-lg flex items-center justify-center">
<span class="text-xl">🧪</span>
</div>
<h2 class="text-2xl font-bold">{t('project.type.experiment')}</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
{experimentProjects.map((project) => (
<div class={`group overflow-hidden border border-${project.color}-500/20 hover:border-${project.color}-500/40 h-full flex flex-col transition-all duration-500 hover:scale-105 bg-white/10 dark:bg-gray-800/50 backdrop-blur-sm rounded-xl relative`}>
<div class="absolute top-4 right-4 z-10 flex gap-2">
<span class={`px-3 py-1 bg-${project.color}-100 dark:bg-${project.color}-800 text-${project.color}-800 dark:text-${project.color}-200 text-xs rounded-full font-medium border border-${project.color}-200 dark:border-${project.color}-700`}>{t(`project.status.${project.status}`)}</span>
</div>
<div class={`h-48 bg-gradient-to-br ${project.image.bg} relative overflow-hidden`}>
<div class={`absolute inset-0 bg-gradient-to-br ${project.image.bg} group-hover:${project.image.hover} transition-all duration-500`}></div>
<div class="absolute bottom-4 left-4 right-4">
<div class={`bg-black/50 backdrop-blur-sm rounded px-3 py-1 text-xs font-mono ${project.image.text}`}>
{project.title}
</div>
</div>
</div>
<div class="p-6 pb-4">
<h3 class={`text-xl group-hover:text-${project.color}-400 transition-colors duration-300 flex items-center gap-2 font-bold mb-4`}>
<span class={`text-${project.color}-500`}>{project.icon}</span>
{project.title}
</h3>
</div>
<div class="px-6 flex-grow">
<div class="space-y-3">
{project.description.slice(0, 2).map((desc) => (
<div class="flex items-start gap-2 text-sm text-muted-foreground group-hover:text-foreground transition-colors duration-300">
<span class={`text-${project.color}-500 mt-1`}>•</span>
<span>{desc}</span>
</div>
))}
</div>
<div class="mt-6 mb-4 flex flex-wrap gap-2">
{project.tech.slice(0, 4).map((tech) => (
<span class="px-2 py-1 bg-gray-500/10 dark:bg-gray-400/20 text-gray-600 dark:text-gray-300 text-xs rounded-md border border-gray-500/20 dark:border-gray-400/30">{tech}</span>
))}
</div>
</div>
<div class="p-6 pt-2">
<a href={project.link} class={`inline-flex items-center text-sm font-medium text-${project.color}-500 hover:text-${project.color}-600 dark:hover:text-${project.color}-400 transition-colors duration-300`}>
{t('project.visit')}
<svg class="ml-1 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
</div>
</div>
))}
</div>
</>
)}
</Container>
</section>
</main>
<Footer lang={lang} client:load />
</Layout>