This commit is contained in:
eric
2026-03-08 12:30:50 -05:00
parent 0e75fb2e6e
commit 57a788006f
12 changed files with 536 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { Link, useTranslation } from "@/i18n/navigation";
import ShopCard from "./ShopCard";
const linkKeys = ["guide", "discord", "tools", "github", "feishu", "destinations"] as const;
const linkIcons: Record<(typeof linkKeys)[number], string> = {
@@ -17,6 +18,11 @@ export default function Community() {
return (
<section id="community" className="py-20 sm:py-28 dark:bg-slate-950">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
{/* 电商独立站卡片 */}
<div id="shop">
<ShopCard />
</div>
{/* 数字游民社区 - 优化布局 */}
<div className="mb-20">
<Link

View File

@@ -32,9 +32,9 @@ const footerSections = [
{
titleKey: "about" as const,
links: [
{ labelKey: "aboutUs" as const, href: "#" },
{ labelKey: "aboutUs" as const, href: "/about", internal: true },
{ labelKey: "contribute" as const, href: "#" },
{ labelKey: "privacy" as const, href: "#" },
{ labelKey: "privacy" as const, href: "/privacy", internal: true, openInNewTab: true },
{ labelKey: "contact" as const, href: "#" },
],
},
@@ -66,12 +66,23 @@ export default function Footer() {
<ul className="space-y-2.5">
{section.links.map((link) => (
<li key={link.labelKey}>
<a
href={link.href}
className="text-sm text-slate-500 transition-colors hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
>
{t(`links.${link.labelKey}`)}
</a>
{"internal" in link && link.internal ? (
<Link
href={link.href}
target={"openInNewTab" in link && link.openInNewTab ? "_blank" : undefined}
rel={"openInNewTab" in link && link.openInNewTab ? "noopener noreferrer" : undefined}
className="text-sm text-slate-500 transition-colors hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
>
{t(`links.${link.labelKey}`)}
</Link>
) : (
<a
href={link.href}
className="text-sm text-slate-500 transition-colors hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
>
{t(`links.${link.labelKey}`)}
</a>
)}
</li>
))}
</ul>

View File

@@ -10,6 +10,7 @@ const navLinks = [
{ labelKey: "roadmap" as const, href: "#roadmap" },
{ labelKey: "tools" as const, href: "#tools" },
{ labelKey: "community" as const, href: "#community" },
{ labelKey: "shop" as const, href: "#shop" },
];
export default function Header() {
@@ -57,13 +58,6 @@ export default function Header() {
{tNav(link.labelKey)}
</a>
))}
<button
type="button"
onClick={() => router.replace(pathname, { locale: locale === "zh" ? "en" : "zh" })}
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
>
{locale === "zh" ? "EN" : "中文"}
</button>
</nav>
<div className="flex shrink-0 items-center gap-1 sm:gap-3">
@@ -87,6 +81,15 @@ export default function Header() {
{t("loginRegister")}
</button>
)}
<button
type="button"
onClick={() => router.replace(pathname, { locale: locale === "zh" ? "en" : "zh" })}
className="inline-flex shrink-0 items-center justify-center rounded-lg p-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
aria-label={locale === "zh" ? "Switch to English" : "切换到中文"}
title={locale === "zh" ? "EN" : "中文"}
>
{locale === "zh" ? "EN" : "中文"}
</button>
<ThemeToggle />
<button
onClick={() => setMobileOpen(!mobileOpen)}

View File

@@ -0,0 +1,47 @@
"use client";
import { useTranslation } from "@/i18n/navigation";
import { getThemeConfig } from "@/config";
export default function ShopCard() {
const { t } = useTranslation("shop");
const config = getThemeConfig();
const shopUrl = config.services?.shopUrl;
if (!shopUrl) return null;
return (
<div className="mb-12">
<a
href={shopUrl}
target="_blank"
rel="noopener noreferrer"
className="group block overflow-hidden rounded-2xl border border-emerald-200/80 bg-gradient-to-br from-emerald-50 via-white to-teal-50/60 p-8 shadow-sm transition-all duration-300 hover:border-emerald-300 hover:shadow-xl hover:shadow-emerald-100/50 dark:border-emerald-800/80 dark:from-emerald-900/30 dark:via-slate-900 dark:to-teal-900/20 dark:hover:border-emerald-700 dark:hover:shadow-emerald-900/30 sm:p-10"
>
<div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
<div className="flex-1">
<div className="mb-3 inline-flex items-center gap-2 rounded-full bg-emerald-100 px-4 py-1.5 text-sm font-semibold text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-300">
<span className="text-lg">🛒</span>
{t("badge")}
</div>
<h3 className="mb-3 text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-3xl">
{t("title")}
</h3>
<p className="mb-4 max-w-xl text-base leading-relaxed text-slate-600 dark:text-slate-400">
{t("desc")}
</p>
</div>
<div className="flex shrink-0 items-center gap-4 rounded-2xl bg-white/90 p-6 shadow-md backdrop-blur-sm transition-transform dark:bg-slate-800/90 group-hover:scale-[1.02] sm:p-8">
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-emerald-400 to-teal-500 text-3xl shadow-lg shadow-emerald-200/50">
🛍
</div>
<div className="text-left">
<div className="font-bold text-slate-800 dark:text-slate-200">{t("cta")}</div>
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">{t("ctaHint")}</div>
</div>
</div>
</div>
</a>
</div>
);
}

View File

@@ -0,0 +1,48 @@
"use client";
const PLATFORM_ICONS: Record<string, React.ReactNode> = {
// 国内平台
douyin: (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-5.2 1.74 2.89 2.89 0 0 1 2.31-4.64 2.93 2.93 0 0 1 .88.13V9.4a6.84 6.84 0 0 0-1-.05A6.33 6.33 0 0 0 5 20.1a6.34 6.34 0 0 0 10.86-4.43v-7a8.16 8.16 0 0 0 4.77 1.52v-3.4a4.85 4.85 0 0 1-1-.1z" />
</svg>
),
"wechat-official": (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M8.5 6C5.46 6 3 8.24 3 11c0 1.64.72 3.14 1.87 4.17L3 17l2.13-1.13A6.5 6.5 0 0 0 8.5 16c3.04 0 5.5-2.24 5.5-5s-2.46-5-5.5-5zm0 8c-1.38 0-2.63-.56-3.54-1.46C4.43 11.54 4 10.31 4 9s.43-2.54 1.46-3.54C6.37 4.56 7.62 4 9 4s2.63.56 3.54 1.46c.93.93 1.46 2.16 1.46 3.54s-.53 2.61-1.46 3.54A4.99 4.99 0 0 1 8.5 14z" />
</svg>
),
xiaohongshu: (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z" />
</svg>
),
// 海外平台
tiktok: (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-5.2 1.74 2.89 2.89 0 0 1 2.31-4.64 2.93 2.93 0 0 1 .88.13V9.4a6.84 6.84 0 0 0-1-.05A6.33 6.33 0 0 0 5 20.1a6.34 6.34 0 0 0 10.86-4.43v-7a8.16 8.16 0 0 0 4.77 1.52v-3.4a4.85 4.85 0 0 1-1-.1z" />
</svg>
),
youtube: (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
),
twitter: (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
),
};
export default function SocialPlatformIcon({ platform }: { platform: string }) {
return (
<span className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-300">
{PLATFORM_ICONS[platform] ?? (
<svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93z" />
</svg>
)}
</span>
);
}