This commit is contained in:
eric
2026-03-09 03:45:10 -05:00
parent 7638e4032e
commit 52da06789b
9 changed files with 896 additions and 79 deletions

View File

@@ -1,4 +1,4 @@
import Footer from "../../components/Footer";
import Footer from "@/app/components/Footer";
export const metadata = {
title: "关于我们 | 游牧中国",

View File

@@ -0,0 +1,272 @@
"use client";
import { use } from "react";
import { useTranslation, Link } from "@/i18n/navigation";
import Footer from "@/app/components/Footer";
interface CityGuide {
slug: string;
nameZh: string;
nameEn: string;
emoji: string;
descriptionZh: string;
descriptionEn: string;
costOfLiving: {
item: string;
priceZh: string;
priceEn: string;
}[];
coworking: {
name: string;
address: string;
price: string;
rating: number;
}[];
neighborhoods: {
name: string;
pros: string;
cons: string;
}[];
tips: string[];
}
const CITY_GUIDES: CityGuide[] = [
{
slug: "dali",
nameZh: "大理",
nameEn: "Dali",
emoji: "🏯",
descriptionZh: "大理是中国最受欢迎的的数字游民目的地之一,以其悠闲的生活方式、美丽的自然风光和实惠的生活成本著称。",
descriptionEn: "Dali is one of China's most popular digital nomad destinations, known for its laid-back lifestyle, beautiful scenery, and affordable cost of living.",
costOfLiving: [
{ item: "租房 (单间)", priceZh: "¥800-1500/月", priceEn: "¥800-1500/month" },
{ item: "共享办公", priceZh: "¥300-500/月", priceEn: "¥300-500/month" },
{ item: "餐饮", priceZh: "¥30-50/天", priceEn: "¥30-50/day" },
{ item: "咖啡", priceZh: "¥15-30/杯", priceEn: "¥15-30/cup" },
],
coworking: [
{ name: "空岛空间", address: "大理古城人民路", price: "¥400/月", rating: 4.8 },
{ name: "Dreamland", address: "大理古城博爱路", price: "¥350/月", rating: 4.6 },
{ name: "柴米多", address: "大理古城叶榆路", price: "¥500/月", rating: 4.7 },
],
neighborhoods: [
{ name: "大理古城", pros: "配套设施齐全,咖啡馆多", cons: "游客较多" },
{ name: "海东", pros: "安静,海景房", cons: "交通不便" },
{ name: "下关市区", pros: "生活便利,性价比高", cons: "缺乏氛围" },
],
tips: [
"推荐10月-次年4月前往天气最佳",
"古城内租房建议提前实地看房",
"办理民宿短租备案更合规",
],
},
{
slug: "chengdu",
nameZh: "成都",
nameEn: "Chengdu",
emoji: "🐼",
descriptionZh: "成都被誉为天府之国,是中国西部的科技中心,也是一座充满生活气息的城市,非常适合数字游民。",
descriptionEn: "Chengdu, known as the 'Land of Abundance', is the tech hub of western China and a city full of life, perfect for digital nomads.",
costOfLiving: [
{ item: "租房 (单间)", priceZh: "¥1200-2500/月", priceEn: "¥1200-2500/month" },
{ item: "共享办公", priceZh: "¥400-800/月", priceEn: "¥400-800/month" },
{ item: "餐饮", priceZh: "¥40-60/天", priceEn: "¥40-60/day" },
{ item: "咖啡", priceZh: "¥20-35/杯", priceEn: "¥20-35/cup" },
],
coworking: [
{ name: "Workfrom成都", address: "高新区天府大道", price: "¥600/月", rating: 4.7 },
{ name: "Feng Coffee", address: "锦江区春熙路", price: "¥500/月", rating: 4.5 },
{ name: "方糖小镇", address: "武侯区科华北路", price: "¥450/月", rating: 4.6 },
],
neighborhoods: [
{ name: "高新区", pros: "科技公司多,年轻人多", cons: "房价较高" },
{ name: "锦江区", pros: "美食众多,生活便利", cons: "较拥挤" },
{ name: "玉林路", pros: "文艺氛围,咖啡馆多", cons: "老小区" },
],
tips: [
"推荐3-5月或9-11月前往",
"高新区互联网公司聚集,便于社交",
"美食之都,餐饮选择丰富",
],
},
{
slug: "shenzhen",
nameZh: "深圳",
nameEn: "Shenzhen",
emoji: "🌃",
descriptionZh: "深圳是中国最具活力的科技创新城市,毗邻香港,是年轻创业者的天堂。",
descriptionEn: "Shenzhen is China's most dynamic tech innovation city, adjacent to Hong Kong, a paradise for young entrepreneurs.",
costOfLiving: [
{ item: "租房 (单间)", priceZh: "¥1500-3500/月", priceEn: "¥1500-3500/month" },
{ item: "共享办公", priceZh: "¥600-1200/月", priceEn: "¥600-1200/month" },
{ item: "餐饮", priceZh: "¥50-80/天", priceEn: "¥50-80/day" },
{ item: "咖啡", priceZh: "¥25-40/杯", priceEn: "¥25-40/cup" },
],
coworking: [
{ name: "WeWork", address: "南山区科技园", price: "¥1000/月", rating: 4.8 },
{ name: "思微SimplyWork", address: "南山区科兴科学园", price: "¥800/月", rating: 4.6 },
{ name: "柴火创客空间", address: "南山区高新南七道", price: "¥700/月", rating: 4.7 },
],
neighborhoods: [
{ name: "南山区", pros: "科技公司集中,氛围好", cons: "房价最高" },
{ name: "福田区", pros: "市中心,交通便利", cons: "缺乏创业氛围" },
{ name: "龙华区", pros: "性价比高", cons: "较偏远" },
],
tips: [
"全年气候宜人,随时适合前往",
"创业氛围浓厚,便于资源对接",
"离香港近,可一日往返",
],
},
{
slug: "hangzhou",
nameZh: "杭州",
nameEn: "Hangzhou",
emoji: "⛲",
descriptionZh: "杭州是中国最美的城市之一,阿里巴巴总部所在地,数字经济发达。",
descriptionEn: "Hangzhou is one of China's most beautiful cities, home to Alibaba, with a developed digital economy.",
costOfLiving: [
{ item: "租房 (单间)", priceZh: "¥1400-2800/月", priceEn: "¥1400-2800/month" },
{ item: "共享办公", priceZh: "¥500-900/月", priceEn: "¥500-900/month" },
{ item: "餐饮", priceZh: "¥45-70/天", priceEn: "¥45-70/day" },
{ item: "咖啡", priceZh: "¥22-38/杯", priceEn: "¥22-38/cup" },
],
coworking: [
{ name: "Binggo咖啡", address: "西湖区文二路", price: "¥600/月", rating: 4.6 },
{ name: "创业邦", address: "滨江区江南大道", price: "¥700/月", rating: 4.5 },
{ name: "福云咖啡", address: "西湖区教工路", price: "¥550/月", rating: 4.4 },
],
neighborhoods: [
{ name: "滨江区", pros: "阿里附近,创业氛围浓", cons: "房价较高" },
{ name: "西湖区", pros: "环境优美,生活便利", cons: "游客较多" },
{ name: "余杭区", pros: "性价比高,阿里新总部", cons: "配套不全" },
],
tips: [
"推荐4-5月前往西湖景色最美",
"阿里巴巴带动数字经济发展",
"生活节奏适中,工作生活平衡好",
],
},
];
export default function CityGuidePage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = use(params);
const { t } = useTranslation("cityGuide");
const city = CITY_GUIDES.find((c) => c.slug === slug);
if (!city) {
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[1200px] mx-auto px-4 py-16 text-center">
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100"></h1>
<Link href="/" className="text-[#ff4d4f] mt-4 inline-block"></Link>
</main>
<Footer />
</div>
);
}
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[1200px] mx-auto px-4 sm:px-6 py-6 sm:py-8">
<Link href="/" className="inline-flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 hover:text-[#ff4d4f] mb-4">
</Link>
<div className="bg-white dark:bg-gray-900 rounded-2xl overflow-hidden shadow-lg border border-gray-100 dark:border-gray-800">
<div className="h-32 sm:h-48 bg-gradient-to-r from-purple-500 to-pink-500 flex items-end p-6 sm:p-8">
<span className="text-5xl sm:text-7xl mb-2">{city.emoji}</span>
<h1 className="text-3xl sm:text-4xl font-bold text-white ml-4">{city.nameZh}</h1>
</div>
<div className="p-6 sm:p-8">
<p className="text-gray-600 dark:text-gray-400 text-base sm:text-lg mb-8">
{city.descriptionZh}
</p>
<section className="mb-8">
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
💰
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{city.costOfLiving.map((item, i) => (
<div key={i} className="flex justify-between items-center p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<span className="text-gray-600 dark:text-gray-400">{item.item}</span>
<span className="font-semibold text-gray-900 dark:text-gray-100">{item.priceZh}</span>
</div>
))}
</div>
</section>
<section className="mb-8">
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
💻
</h2>
<div className="space-y-3">
{city.coworking.map((cw, i) => (
<div key={i} className="flex flex-col sm:flex-row sm:items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg gap-2">
<div>
<div className="font-semibold text-gray-900 dark:text-gray-100">{cw.name}</div>
<div className="text-sm text-gray-500 dark:text-gray-400">{cw.address}</div>
</div>
<div className="flex items-center gap-3">
<span className="text-[#ff4d4f] font-medium">{cw.price}</span>
<span className="text-amber-500"> {cw.rating}</span>
</div>
</div>
))}
</div>
</section>
<section className="mb-8">
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
🏠
</h2>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
{city.neighborhoods.map((n, i) => (
<div key={i} className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="font-semibold text-gray-900 dark:text-gray-100 mb-2">{n.name}</div>
<div className="text-sm text-green-600 dark:text-green-400 mb-1"> {n.pros}</div>
<div className="text-sm text-red-500 dark:text-red-400"> {n.cons}</div>
</div>
))}
</div>
</section>
<section className="mb-8">
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
💡 Tips
</h2>
<ul className="space-y-2">
{city.tips.map((tip, i) => (
<li key={i} className="flex items-start gap-2 text-gray-600 dark:text-gray-400">
<span className="text-[#ff4d4f]"></span>
{tip}
</li>
))}
</ul>
</section>
<div className="flex flex-col sm:flex-row gap-3">
<Link
href="/join"
className="flex-1 text-center bg-[#ff4d4f] text-white py-3 px-6 rounded-full font-medium hover:bg-[#ff7a45] transition-colors"
>
</Link>
<Link
href="/meetups"
className="flex-1 text-center bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 py-3 px-6 rounded-full font-medium hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
>
线
</Link>
</div>
</div>
</div>
</main>
<Footer />
</div>
);
}

View File

@@ -0,0 +1,236 @@
"use client";
import { useState } from "react";
import { useTranslation, Link } from "@/i18n/navigation";
import Footer from "@/app/components/Footer";
interface Topic {
id: string;
title: string;
author: string;
avatar: string;
views: number;
replies: number;
likes: number;
category: string;
lastActive: string;
pinned: boolean;
}
const MOCK_TOPICS: Topic[] = [
{
id: "1",
title: "大理最佳共享办公空间推荐 Top 10",
author: "林晓",
avatar: "https://i.pravatar.cc/150?img=5",
views: 2340,
replies: 56,
likes: 128,
category: "资源共享",
lastActive: "2小时前",
pinned: true,
},
{
id: "2",
title: "2026年数字游民友好城市排名",
author: "张浩",
avatar: "https://i.pravatar.cc/150?img=3",
views: 1890,
replies: 42,
likes: 95,
category: "城市测评",
lastActive: "5小时前",
pinned: true,
},
{
id: "3",
title: "远程工作者的税务规划指南",
author: "陈悦",
avatar: "https://i.pravatar.cc/150?img=9",
views: 1560,
replies: 38,
likes: 87,
category: "税务签证",
lastActive: "1天前",
pinned: false,
},
{
id: "4",
title: "成都 vs 大理:哪个更适合游民?",
author: "周杰",
avatar: "https://i.pravatar.cc/150?img=7",
views: 1230,
replies: 67,
likes: 54,
category: "城市对比",
lastActive: "3小时前",
pinned: false,
},
{
id: "5",
title: "新手游民必备工具与装备清单",
author: "吴婷",
avatar: "https://i.pravatar.cc/150?img=1",
views: 980,
replies: 29,
likes: 76,
category: "经验分享",
lastActive: "1天前",
pinned: false,
},
{
id: "6",
title: "如何在泰国办理长期签证?",
author: "杨帅",
avatar: "https://i.pravatar.cc/150?img=12",
views: 2150,
replies: 89,
likes: 156,
category: "税务签证",
lastActive: "30分钟前",
pinned: false,
},
{
id: "7",
title: "数字游民保险选择指南",
author: "赵琪",
avatar: "https://i.pravatar.cc/150?img=16",
views: 1780,
replies: 45,
likes: 112,
category: "安全保障",
lastActive: "6小时前",
pinned: false,
},
{
id: "8",
title: "东南亚最佳数字游民目的地",
author: "黄磊",
avatar: "https://i.pravatar.cc/150?img=11",
views: 3210,
replies: 78,
likes: 234,
category: "目的地",
lastActive: "1小时前",
pinned: false,
},
];
const CATEGORIES = [
{ key: "all", zh: "全部", en: "All" },
{ key: "城市测评", zh: "城市测评", en: "City Review" },
{ key: "城市对比", zh: "城市对比", en: "City Comparison" },
{ key: "税务签证", zh: "税务签证", en: "Tax & Visa" },
{ key: "资源共享", zh: "资源共享", en: "Resource Sharing" },
{ key: "经验分享", zh: "经验分享", en: "Experience" },
{ key: "安全保障", zh: "安全保障", en: "Safety & Insurance" },
{ key: "目的地", zh: "目的地", en: "Destinations" },
];
export default function DiscussPage() {
const { t } = useTranslation("discuss");
const [activeCategory, setActiveCategory] = useState("all");
const [searchQuery, setSearchQuery] = useState("");
const filteredTopics = MOCK_TOPICS.filter((topic) => {
const matchesCategory = activeCategory === "all" || topic.category === activeCategory;
const matchesSearch = topic.title.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[1200px] mx-auto px-4 sm:px-6 py-6 sm:py-8">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
{t("title")}
</h1>
<p className="text-gray-500 dark:text-gray-400 mt-1 text-sm sm:text-base">
{t("subtitle")}
</p>
</div>
<button className="bg-[#ff4d4f] text-white px-4 sm:px-6 py-2 sm:py-2.5 rounded-full font-medium text-sm hover:bg-[#ff7a45] transition-colors">
{t("newPost")}
</button>
</div>
<div className="mb-6">
<input
type="text"
placeholder={t("searchPlaceholder")}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f]"
/>
</div>
<div className="flex flex-wrap gap-2 mb-6">
{CATEGORIES.map((cat) => (
<button
key={cat.key}
onClick={() => setActiveCategory(cat.key)}
className={`px-3 sm:px-4 py-1.5 sm:py-2 rounded-full text-xs sm:text-sm font-medium transition-colors ${
activeCategory === cat.key
? "bg-[#ff4d4f] text-white"
: "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
}`}
>
{cat.zh}
</button>
))}
</div>
<div className="space-y-3 sm:space-y-4">
{filteredTopics.map((topic) => (
<div
key={topic.id}
className="bg-white dark:bg-gray-900 rounded-xl p-4 sm:p-5 shadow-sm border border-gray-100 dark:border-gray-800 hover:shadow-md transition-shadow cursor-pointer"
>
<div className="flex items-start gap-3 sm:gap-4">
<img
src={topic.avatar}
alt={topic.author}
className="w-10 h-10 sm:w-12 sm:h-12 rounded-full object-cover shrink-0"
/>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
{topic.pinned && (
<span className="text-[#ff4d4f] text-xs font-medium">📌</span>
)}
<span className="text-xs text-[#ff4d4f] bg-red-50 dark:bg-red-900/20 px-2 py-0.5 rounded-full">
{topic.category}
</span>
</div>
<h3 className="font-semibold text-gray-900 dark:text-gray-100 text-sm sm:text-base mb-2 line-clamp-2">
{topic.title}
</h3>
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-gray-500 dark:text-gray-400">
<span>{topic.author}</span>
<span className="hidden sm:inline">·</span>
<span>{topic.lastActive}</span>
<span className="hidden sm:inline">·</span>
<span>👁 {topic.views}</span>
<span>💬 {topic.replies}</span>
<span> {topic.likes}</span>
</div>
</div>
</div>
</div>
))}
</div>
{filteredTopics.length === 0 && (
<div className="text-center py-12 sm:py-16">
<span className="text-4xl sm:text-5xl mb-4 block">💬</span>
<p className="text-gray-500 dark:text-gray-400 text-base sm:text-lg">
{t("noResults")}
</p>
</div>
)}
</main>
<Footer />
</div>
);
}

View File

@@ -1,4 +1,4 @@
import Footer from "../../components/Footer";
import Footer from "@/app/components/Footer";
export const metadata = {
title: "招聘 | 游牧中国",

View File

@@ -1,13 +1,13 @@
"use client";
import { useState, useMemo } from "react";
import { Link, useLocale } from "@/i18n/navigation";
import { Link, useLocale, useRouter } from "@/i18n/navigation";
import { useTranslation } from "@/i18n/navigation";
import HeroSection from "../components/HeroSection";
import FilterBar from "../components/FilterBar";
import CityCard from "../components/CityCard";
import CityModal from "../components/CityModal";
import Footer from "../components/Footer";
import Footer from "@/app/components/Footer";
import { cities, type City } from "../data/cities";
import { HOME_CONFIG } from "@/config";
@@ -23,6 +23,7 @@ export default function Home() {
const [sortBy, setSortBy] = useState("score");
const [cityModal, setCityModal] = useState<City | null>(null);
const locale = useLocale();
const router = useRouter();
const { t } = useTranslation("home");
const { t: tNav } = useTranslation("nav");
@@ -52,6 +53,13 @@ export default function Home() {
return result;
}, [activeFilter, sortBy]);
const cityTags = [
{ key: "dali", emoji: "🏯", zh: "大理", en: "Dali" },
{ key: "chengdu", emoji: "🐼", zh: "成都", en: "Chengdu" },
{ key: "shenzhen", emoji: "🌃", zh: "深圳", en: "Shenzhen" },
{ key: "hangzhou", emoji: "⛲", zh: "杭州", en: "Hangzhou" },
];
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<HeroSection />
@@ -125,7 +133,7 @@ export default function Home() {
</div>
</Link>
<Link href="/homeMeetups" className="right-item block">
<Link href="/meetups" className="right-item block">
<div className="right-item-header">{t("latestEvents")}</div>
<div className="p-1.5 sm:p-4 flex flex-col gap-0.5 sm:gap-2.5 text-[10px] sm:text-sm text-gray-700 dark:text-gray-300 h-[calc(100%-38px)] sm:h-[calc(100%-42px)]">
<div className="flex items-center gap-1 bg-[#f5f6f7] dark:bg-gray-800 rounded-lg px-1.5 py-1 sm:px-3 sm:py-2 font-medium text-gray-900 dark:text-gray-100">
@@ -182,44 +190,47 @@ export default function Home() {
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5">
{routes.map((route) => (
<div
key={route.title}
className="community-card group cursor-pointer"
<Link
key={route.titleZh}
href={`/dashboard?route=${encodeURIComponent(locale === "zh" ? route.titleZh : route.titleEn)}`}
className="community-card group cursor-pointer block"
>
<div className={`h-2 bg-gradient-to-r ${route.gradient}`} />
<div className="p-4 sm:p-5">
<div className="flex items-center justify-between mb-3">
<h3 className="font-bold text-gray-900 dark:text-gray-100 text-sm">
{route.title}
{locale === "zh" ? route.titleZh : route.titleEn}
</h3>
<span className="text-[11px] text-gray-400 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 rounded-full px-2 py-0.5">
{route.duration}
{locale === "zh" ? route.durationZh : route.durationEn}
</span>
</div>
<div className="flex items-center gap-1 mb-3">
{route.cities.map((city, i) => (
<span key={city} className="flex items-center gap-0.5 text-sm">
<span>{route.emojis[i]}</span>
<span className="font-medium text-gray-700 dark:text-gray-300">{city}</span>
{i < route.cities.length - 1 && (
{route.emojis.map((emoji, i) => (
<span key={i} className="flex items-center gap-0.5 text-sm">
<span>{emoji}</span>
<span className="font-medium text-gray-700 dark:text-gray-300">
{locale === "zh" ? route.citiesZh[i] : route.citiesEn[i]}
</span>
{i < route.emojis.length - 1 && (
<span className="text-gray-300 dark:text-gray-600 mx-1"></span>
)}
</span>
))}
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mb-3 leading-relaxed">
{route.desc}
{locale === "zh" ? route.descZh : route.descEn}
</p>
<div className="flex items-center justify-between">
<span className="text-sm font-semibold text-gray-900 dark:text-gray-100">
{route.cost}
{locale === "zh" ? route.costZh : route.costEn}
</span>
<span className="text-xs text-[#ff4d4f] font-medium group-hover:translate-x-1 transition-transform">
{t("routeDetails")}
</span>
</div>
</div>
</div>
</Link>
))}
</div>
</section>
@@ -231,7 +242,7 @@ export default function Home() {
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5">
<div className="community-card">
<Link href="/meetups" className="community-card block">
<div className="p-4 sm:p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="font-bold text-gray-900 dark:text-gray-100 text-sm flex items-center gap-1.5">
@@ -257,14 +268,11 @@ export default function Home() {
</div>
))}
</div>
<Link
href="/homeMeetups"
className="block w-full text-center text-xs font-medium text-[#ff4d4f] hover:text-[#ff7a45] mt-4 pt-3 border-t border-gray-100 dark:border-gray-700 transition-colors"
>
<div className="block w-full text-center text-xs font-medium text-[#ff4d4f] hover:text-[#ff7a45] mt-4 pt-3 border-t border-gray-100 dark:border-gray-700 transition-colors">
{t("viewMore")}
</Link>
</div>
</div>
</div>
</Link>
<div className="community-card">
<div className="p-4 sm:p-5">
@@ -290,35 +298,35 @@ export default function Home() {
</div>
</div>
<div className="community-card">
<Link href="/discuss" className="community-card block">
<div className="p-4 sm:p-5">
<h3 className="font-bold text-gray-900 dark:text-gray-100 text-sm flex items-center gap-1.5 mb-4">
🔥 {t("hotDiscussions")}
</h3>
<div className="space-y-3">
{hotTopics.map((t_item, i) => (
<a key={t_item.title} href="#" className="flex items-start gap-2.5 group">
<div key={t_item.titleZh} className="flex items-start gap-2.5 group">
<span className={`text-xs font-bold mt-0.5 w-4 shrink-0 ${i < 3 ? "text-[#ff4d4f]" : "text-gray-300 dark:text-gray-600"}`}>
{i + 1}
</span>
<div className="flex-1">
<p min-w-0 className="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors line-clamp-1 leading-snug">
{t_item.title}
<p className="min-w-0 text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors line-clamp-1 leading-snug">
{locale === "zh" ? t_item.titleZh : t_item.titleEn}
</p>
<p className="text-[11px] text-gray-400 dark:text-gray-500 mt-0.5">
{t_item.views.toLocaleString()} {t("views")}
</p>
</div>
</a>
</div>
))}
</div>
<button className="w-full text-center text-xs font-medium text-[#ff4d4f] hover:text-[#ff7a45] mt-4 pt-3 border-t border-gray-100 dark:border-gray-700 transition-colors">
{t("viewMore")}
</button>
<div className="block w-full text-center text-xs font-medium text-[#ff4d4f] hover:text-[#ff7a45] mt-4 pt-3 border-t border-gray-100 dark:border-gray-700 transition-colors">
{t("viewAllTopics")}
</div>
</div>
</div>
</Link>
<div className="community-card bg-gradient-to-br from-emerald-50 to-teal-50 dark:from-emerald-950/30 dark:to-teal-950/30">
<Link href="/services" className="community-card bg-gradient-to-br from-emerald-50 to-teal-50 dark:from-emerald-950/30 dark:to-teal-950/30 block">
<div className="p-4 sm:p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">🛡</span>
@@ -330,13 +338,13 @@ export default function Home() {
<p className="text-xs text-emerald-700 dark:text-emerald-400 bg-emerald-100/50 dark:bg-emerald-900/30 rounded-lg px-3 py-2 mb-3">
{t("insuranceCoverage")}
</p>
<button className="w-full text-center text-sm font-semibold text-emerald-700 dark:text-emerald-400 hover:text-emerald-800 dark:hover:text-emerald-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-emerald-200 dark:border-emerald-800 rounded-full py-2 transition-colors">
<div className="w-full text-center text-sm font-semibold text-emerald-700 dark:text-emerald-400 hover:text-emerald-800 dark:hover:text-emerald-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-emerald-200 dark:border-emerald-800 rounded-full py-2 transition-colors">
{t("learnMore")}
</button>
</div>
</div>
</div>
</Link>
<div className="community-card bg-gradient-to-br from-violet-50 to-purple-50 dark:from-violet-950/30 dark:to-purple-950/30">
<Link href="/join" className="community-card bg-gradient-to-br from-violet-50 to-purple-50 dark:from-violet-950/30 dark:to-purple-950/30 block">
<div className="p-4 sm:p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">💬</span>
@@ -358,13 +366,13 @@ export default function Home() {
</div>
<span className="text-xs text-gray-400 dark:text-gray-500">{t("onlineNow")}</span>
</div>
<button className="w-full text-center text-sm font-semibold text-violet-700 dark:text-violet-400 hover:text-violet-800 dark:hover:text-violet-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-violet-200 dark:border-violet-800 rounded-full py-2 transition-colors">
<div className="w-full text-center text-sm font-semibold text-violet-700 dark:text-violet-400 hover:text-violet-800 dark:hover:text-violet-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-violet-200 dark:border-violet-800 rounded-full py-2 transition-colors">
{t("joinChat")}
</button>
</div>
</div>
</div>
</Link>
<div className="community-card bg-gradient-to-br from-amber-50 to-orange-50 dark:from-amber-950/30 dark:to-orange-950/30">
<Link href="/tools" className="community-card bg-gradient-to-br from-amber-50 to-orange-50 dark:from-amber-950/30 dark:to-orange-950/30 block">
<div className="p-4 sm:p-5">
<div className="flex items-start gap-3 mb-3">
<span className="text-2xl">🏛</span>
@@ -376,20 +384,21 @@ export default function Home() {
</div>
</div>
<div className="flex flex-wrap gap-1.5 mb-3">
{["🏯 大理", "🐼 成都", "🌃 深圳", "⛲ 杭州"].map((tag) => (
<span
key={tag}
className="text-[11px] bg-amber-100/60 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 rounded-full px-2 py-0.5"
{cityTags.map((tag) => (
<button
key={tag.key}
onClick={() => router.push(`/city/${tag.key}`)}
className="text-[11px] bg-amber-100/60 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 rounded-full px-2 py-0.5 cursor-pointer hover:bg-amber-200 dark:hover:bg-amber-800 transition-colors"
>
{tag}
</span>
{tag.emoji} {locale === "zh" ? tag.zh : tag.en}
</button>
))}
</div>
<button className="w-full text-center text-sm font-semibold text-amber-700 dark:text-amber-400 hover:text-amber-800 dark:hover:text-amber-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-amber-200 dark:border-amber-800 rounded-full py-2 transition-colors">
<div className="w-full text-center text-sm font-semibold text-amber-700 dark:text-amber-400 hover:text-amber-800 dark:hover:text-amber-300 bg-white/80 dark:bg-gray-800/80 hover:bg-white dark:hover:bg-gray-800 border border-amber-200 dark:border-amber-800 rounded-full py-2 transition-colors">
{t("viewGuides")}
</button>
</div>
</div>
</div>
</Link>
</div>
</section>

View File

@@ -0,0 +1,224 @@
"use client";
import { useState } from "react";
import { useTranslation, Link } from "@/i18n/navigation";
import Footer from "@/app/components/Footer";
interface Service {
id: string;
icon: string;
titleZh: string;
titleEn: string;
descZh: string;
descEn: string;
features: { zh: string; en: string }[];
priceZh: string;
priceEn: string;
color: string;
gradientFrom: string;
gradientTo: string;
}
const SERVICES: Service[] = [
{
id: "insurance",
icon: "🛡️",
titleZh: "旅行保险",
titleEn: "Travel Insurance",
descZh: "数字游民定制方案,覆盖全球",
descEn: "Global coverage for digital nomads",
features: [
{ zh: "覆盖中国 + 境外", en: "Coverage in China + overseas" },
{ zh: "远程工作专属条款", en: "Remote work specific terms" },
{ zh: "医疗直付服务", en: "Direct medical payment" },
{ zh: "24/7 中文客服", en: "24/7 Chinese support" },
],
priceZh: "¥15/天起",
priceEn: "From ¥15/day",
color: "emerald",
gradientFrom: "from-emerald-500",
gradientTo: "to-teal-500",
},
{
id: "chat",
icon: "💬",
titleZh: "社区聊天",
titleEn: "Community Chat",
descZh: "与全球数字游民实时交流",
descEn: "Connect with nomads worldwide in real-time",
features: [
{ zh: "3,200+ 在线成员", en: "3,200+ members online" },
{ zh: "城市群/兴趣群", en: "City & interest groups" },
{ zh: "即时信息对接", en: "Instant networking" },
{ zh: "经验分享互助", en: "Experience sharing" },
],
priceZh: "免费",
priceEn: "Free",
color: "violet",
gradientFrom: "from-violet-500",
gradientTo: "to-purple-500",
},
{
id: "tools",
icon: "🧰",
titleZh: "工具箱",
titleEn: "Tools",
descZh: "汇率、税务、签证一站式",
descEn: "Exchange, tax & visa in one place",
features: [
{ zh: "实时汇率计算", en: "Real-time exchange rates" },
{ zh: "税务估算器", en: "Tax calculator" },
{ zh: "签证倒计时", en: "Visa countdown" },
{ zh: "FIRE 计算器", en: "FIRE calculator" },
],
priceZh: "免费",
priceEn: "Free",
color: "blue",
gradientFrom: "from-blue-500",
gradientTo: "to-cyan-500",
},
{
id: "coworking",
icon: "🏢",
titleZh: "共享办公",
titleEn: "Coworking",
descZh: "全球优质办公空间推荐",
descEn: "Premium workspace recommendations",
features: [
{ zh: "全球 10,000+ 空间", en: "10,000+ spaces worldwide" },
{ zh: "会员专属折扣", en: "Member discounts" },
{ zh: "实地考察报告", en: "On-site reviews" },
{ zh: "预订直通车", en: "Direct booking" },
],
priceZh: "¥99/月起",
priceEn: "From ¥99/month",
color: "orange",
gradientFrom: "from-orange-500",
gradientTo: "to-amber-500",
},
{
id: "visa",
icon: "📋",
titleZh: "签证服务",
titleEn: "Visa Services",
descZh: "专业签证顾问全程护航",
descEn: "Professional visa consultants",
features: [
{ zh: "全球签证办理", en: "Global visa processing" },
{ zh: "材料翻译公证", en: "Translation & notarization" },
{ zh: "加急服务", en: "Expedited service" },
{ zh: "拒签退款保障", en: "Refund guarantee" },
],
priceZh: "咨询报价",
priceEn: "Contact for quote",
color: "rose",
gradientFrom: "from-rose-500",
gradientTo: "to-pink-500",
},
{
id: "mentor",
icon: "🎓",
titleZh: "导师计划",
titleEn: "Mentor Program",
descZh: "资深游民一对一指导",
descEn: "1-on-1 guidance from experienced nomads",
features: [
{ zh: "职业规划建议", en: "Career planning" },
{ zh: "创业经验分享", en: "Startup experience" },
{ zh: "远程工作技巧", en: "Remote work tips" },
{ zh: "人脉资源对接", en: "Network access" },
],
priceZh: "¥299/次",
priceEn: "¥299/session",
color: "indigo",
gradientFrom: "from-indigo-500",
gradientTo: "to-violet-500",
},
];
export default function ServicesPage() {
const { t } = useTranslation("services");
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[1200px] mx-auto px-4 sm:px-6 py-6 sm:py-8">
<div className="text-center mb-10 sm:mb-12">
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3">
{t("title")}
</h1>
<p className="text-gray-500 dark:text-gray-400 text-sm sm:text-base">
{t("subtitle")}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 sm:gap-6">
{SERVICES.map((service) => (
<div
key={service.id}
className="bg-white dark:bg-gray-900 rounded-2xl overflow-hidden shadow-sm border border-gray-100 dark:border-gray-800 hover:shadow-lg transition-shadow"
>
<div className={`h-2 bg-gradient-to-r ${service.gradientFrom} ${service.gradientTo}`} />
<div className="p-5 sm:p-6">
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3">
<span className="text-3xl">{service.icon}</span>
<div>
<h3 className="font-bold text-gray-900 dark:text-gray-100 text-lg">
{service.titleZh}
</h3>
<p className="text-xs text-gray-500 dark:text-gray-400">
{service.titleEn}
</p>
</div>
</div>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{service.descZh}
</p>
<ul className="space-y-2 mb-5">
{service.features.map((feature, i) => (
<li key={i} className="flex items-center gap-2 text-xs sm:text-sm text-gray-500 dark:text-gray-400">
<span className="text-[#ff4d4f]"></span>
{feature.zh}
</li>
))}
</ul>
<div className="flex items-center justify-between pt-4 border-t border-gray-100 dark:border-gray-800">
<span className="font-bold text-gray-900 dark:text-gray-100">
{service.priceZh}
</span>
<Link
href="/join"
className="text-sm font-medium text-[#ff4d4f] hover:text-[#ff7a45] transition-colors"
>
{t("learnMore")}
</Link>
</div>
</div>
</div>
))}
</div>
<div className="mt-12 sm:mt-16 bg-gradient-to-r from-[#ff4d4f] to-[#ff7a45] rounded-2xl p-6 sm:p-10 text-center">
<h2 className="text-xl sm:text-2xl font-bold text-white mb-3">
</h2>
<p className="text-white/80 mb-5 text-sm sm:text-base">
</p>
<Link
href="/contact"
className="inline-block bg-white text-[#ff4d4f] px-6 py-2.5 rounded-full font-medium hover:bg-gray-100 transition-colors"
>
</Link>
</div>
</main>
<Footer />
</div>
);
}

View File

@@ -21,17 +21,23 @@ export interface Meetup {
}
export interface HotTopic {
title: string;
titleZh: string;
titleEn: string;
views: number;
}
export interface Route {
title: string;
cities: string[];
titleZh: string;
titleEn: string;
citiesZh: string[];
citiesEn: string[];
emojis: string[];
duration: string;
cost: string;
desc: string;
durationZh: string;
durationEn: string;
costZh: string;
costEn: string;
descZh: string;
descEn: string;
gradient: string;
}
@@ -83,39 +89,54 @@ export const MEETUPS: Meetup[] = [
];
export const HOT_TOPICS: HotTopic[] = [
{ title: "大理最佳共享办公空间推荐 Top 10", views: 2340 },
{ title: "2026年数字游民友好城市排名", views: 1890 },
{ title: "远程工作者的税务规划指南", views: 1560 },
{ title: "成都 vs 大理:哪个更适合游民?", views: 1230 },
{ title: "新手游民必备工具与装备清单", views: 980 },
{ titleZh: "大理最佳共享办公空间推荐 Top 10", titleEn: "Top 10 Coworking Spaces in Dali", views: 2340 },
{ titleZh: "2026年数字游民友好城市排名", titleEn: "2026 Digital Nomad Friendly Cities Ranking", views: 1890 },
{ titleZh: "远程工作者的税务规划指南", titleEn: "Tax Planning Guide for Remote Workers", views: 1560 },
{ titleZh: "成都 vs 大理:哪个更适合游民?", titleEn: "Chengdu vs Dali: Which is Better for Nomads?", views: 1230 },
{ titleZh: "新手游民必备工具与装备清单", titleEn: "Essential Tools & Equipment for New Nomads", views: 980 },
];
export const ROUTES: Route[] = [
{
title: "云南慢生活线",
cities: ["昆明", "大理", "丽江"],
titleZh: "云南慢生活线",
titleEn: "Yunnan Slow Life Route",
citiesZh: ["昆明", "大理", "丽江"],
citiesEn: ["Kunming", "Dali", "Lijiang"],
emojis: ["🌸", "🏯", "🏔️"],
duration: "3-6个月",
cost: "¥3,200/月起",
desc: "从春城昆明出发,沿着滇西北一路感受最纯粹的慢生活",
durationZh: "3-6个月",
durationEn: "3-6 months",
costZh: "¥3,200/月起",
costEn: "From ¥3,200/month",
descZh: "从春城昆明出发,沿着滇西北一路感受最纯粹的慢生活",
descEn: "From spring city Kunming, experience the purest slow life along northwest Yunnan",
gradient: "from-purple-500 to-pink-500",
},
{
title: "科技创业线",
cities: ["深圳", "杭州", "上海"],
titleZh: "科技创业线",
titleEn: "Tech Startup Route",
citiesZh: ["深圳", "杭州", "上海"],
citiesEn: ["Shenzhen", "Hangzhou", "Shanghai"],
emojis: ["🌃", "⛲", "🏙️"],
duration: "2-4个月",
cost: "¥7,000/月起",
desc: "穿越中国三大科技中心,连接最前沿的创业生态",
durationZh: "2-4个月",
durationEn: "2-4 months",
costZh: "¥7,000/月起",
costEn: "From ¥7,000/month",
descZh: "穿越中国三大科技中心,连接最前沿的创业生态",
descEn: "Cross China's three tech hubs, connect with cutting-edge startup ecosystem",
gradient: "from-blue-500 to-cyan-500",
},
{
title: "美食文化线",
cities: ["成都", "重庆", "长沙"],
titleZh: "美食文化线",
titleEn: "Food Culture Route",
citiesZh: ["成都", "重庆", "长沙"],
citiesEn: ["Chengdu", "Changsha", "Chongqing"],
emojis: ["🐼", "🌶️", "🎪"],
duration: "2-3个月",
cost: "¥4,000/月起",
desc: "巴蜀到湘楚,用味蕾丈量中国最火辣的城市",
durationZh: "2-3个月",
durationEn: "2-3 months",
costZh: "¥4,000/月起",
costEn: "From ¥4,000/month",
descZh: "巴蜀到湘楚,用味蕾丈量中国最火辣的城市",
descEn: "From Sichuan to Hunan, explore China's spiciest cities with your taste buds",
gradient: "from-orange-500 to-red-500",
},
];

View File

@@ -97,7 +97,9 @@
"perMonth": "/month",
"travelingNow": "Traveling Now",
"hotDiscussions": "Hot Discussions",
"viewAllTopics": "View all topics →",
"views": "views",
"routeDetails": "View route details →",
"viewMore": "View more →",
"travelInsurance": "Travel Insurance",
"travelInsuranceDesc": "Nomad-customized plans",
@@ -356,5 +358,30 @@
"regenerate": "Regenerate",
"subtitle": "Countries visited? Avg monthly cost?",
"generate": "Generate report"
},
"discuss": {
"title": "Discussions",
"subtitle": "Share experiences and connect",
"newPost": "New Post",
"searchPlaceholder": "Search discussions...",
"noResults": "No discussions yet, start the first one!",
"replies": "replies",
"views": "views"
},
"cityGuide": {
"costOfLiving": "Cost of Living",
"coworking": "Coworking",
"neighborhoods": "Neighborhoods",
"tips": "Tips",
"joinCommunity": "Join Local Community",
"viewEvents": "View Events",
"notFound": "City guide not found",
"backToHome": "Back to Home"
},
"services": {
"title": "Services",
"subtitle": "Premium services for digital nomads",
"learnMore": "Learn More",
"comingSoon": "Coming Soon"
}
}

View File

@@ -97,7 +97,9 @@
"perMonth": "场/月",
"travelingNow": "正在旅行",
"hotDiscussions": "热门讨论",
"viewAllTopics": "查看全部话题 →",
"views": "次浏览",
"routeDetails": "查看路线详情 →",
"viewMore": "查看更多 →",
"travelInsurance": "旅行保险",
"travelInsuranceDesc": "数字游民定制方案",
@@ -356,5 +358,31 @@
"regenerate": "重新生成",
"subtitle": "今年飞了多少国家?平均月生活成本?",
"generate": "生成年报"
},
"discuss": {
"title": "热门讨论",
"subtitle": "分享经验、交流心得",
"newPost": "发布新帖",
"searchPlaceholder": "搜索讨论话题...",
"noResults": "暂无讨论,来发起第一个话题吧",
"replies": "回复",
"views": "浏览"
},
"cityGuide": {
"costOfLiving": "生活成本",
"coworking": "共享办公",
"neighborhoods": "社区推荐",
"tips": "实用Tips",
"joinCommunity": "加入当地社群",
"viewEvents": "查看线下活动",
"notFound": "城市指南不存在",
"backToHome": "返回首页"
},
"services": {
"title": "增值服务",
"subtitle": "数字游民专属增值服务",
"learnMore": "了解更多",
"comingSoon": "即将上线",
"contactSupport": "联系客服"
}
}