|
|
@ -0,0 +1,764 @@
|
|
|
|
|
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect } from 'react'
|
|
|
|
|
|
|
|
import { Search, User, MapPin, Calendar, Gift, Heart, MessageCircle, Share2, Bell, Settings, TrendingUp, Users, Compass, Camera, Menu, ChevronDown, Loader2, DollarSign, Mail, Phone, Globe, Instagram, Cake, Briefcase, Languages, Plane, X, Link } from 'lucide-react'
|
|
|
|
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
|
|
|
import { Input } from "@/components/ui/input"
|
|
|
|
|
|
|
|
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card"
|
|
|
|
|
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
|
|
|
|
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
|
|
|
|
|
|
import { Badge } from "@/components/ui/badge"
|
|
|
|
|
|
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
|
|
|
|
|
|
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
|
|
|
|
|
|
|
import { Progress } from "@/components/ui/progress"
|
|
|
|
|
|
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
|
|
|
|
|
|
import { User as UserType } from '@/lib/userDatabase'
|
|
|
|
|
|
|
|
import { userDatabase } from '@/lib/userDatabase'
|
|
|
|
|
|
|
|
import { CarouselSection } from '@/components/CarouselSection'
|
|
|
|
|
|
|
|
import { calculateSimilarity } from '@/lib/matchingAlgorithm';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { 个人资料 } from '@/components/个人资料'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function 首页() {
|
|
|
|
|
|
|
|
// 定义一个渐变背景样式
|
|
|
|
|
|
|
|
const gradientBackground = `
|
|
|
|
|
|
|
|
// 从左上角到右下角的渐变,颜色从浅靛蓝过渡到浅紫,最后到浅粉
|
|
|
|
|
|
|
|
bg-gradient-to-br from-indigo-200 via-purple-200 to-pink-200
|
|
|
|
|
|
|
|
// 从右下角到左上角的渐变,颜色从浅黄过渡到浅红,最后到浅粉
|
|
|
|
|
|
|
|
// bg-blend-overlay 使这个渐变与前一个渐变混合
|
|
|
|
|
|
|
|
bg-gradient-to-tl from-yellow-100 via-red-100 to-pink-100 bg-blend-overlay
|
|
|
|
|
|
|
|
// 将背景不透明度设置为50%,使其半透明
|
|
|
|
|
|
|
|
bg-opacity-50
|
|
|
|
|
|
|
|
// 应用一个动画效果,可能是使渐变在水平方向上移动
|
|
|
|
|
|
|
|
animate-gradient-x
|
|
|
|
|
|
|
|
// 确保背景至少占满整个屏幕高度
|
|
|
|
|
|
|
|
min-h-screen
|
|
|
|
|
|
|
|
// 设置文本颜色为深灰色
|
|
|
|
|
|
|
|
text-gray-800
|
|
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
const [目的地, set目的地] = useState('')
|
|
|
|
|
|
|
|
const [出行日期, set出行日期] = useState('')
|
|
|
|
|
|
|
|
const [预算, set预算] = useState('')
|
|
|
|
|
|
|
|
const [isMatching, setIsMatching] = useState(false)
|
|
|
|
|
|
|
|
const [matchResult, setMatchResult] = useState<UserType | null>(null)
|
|
|
|
|
|
|
|
const [matchingProgress, setMatchingProgress] = useState(0)
|
|
|
|
|
|
|
|
const [users, setUsers] = useState<UserType[]>([])
|
|
|
|
|
|
|
|
const [selectedEvent, setSelectedEvent] = useState<string | null>(null)
|
|
|
|
|
|
|
|
const [selectedInspirationIndex, setSelectedInspirationIndex] = useState<number | null>(null)
|
|
|
|
|
|
|
|
const [showProfile, setShowProfile] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [posts, setPosts] = useState([
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 1,
|
|
|
|
|
|
|
|
user: '小明',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
|
|
|
|
|
|
|
location: '巴黎',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1499856871958-5b9627545d1a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '巴黎的魅力无处不在!从埃菲尔铁塔到卢浮宫,每一步都是艺术的洗礼。分享我在这座浪漫之都的精彩瞬间! #巴黎梦 #艺术之旅',
|
|
|
|
|
|
|
|
likes: 328,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 2,
|
|
|
|
|
|
|
|
user: '小红',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=774&q=80',
|
|
|
|
|
|
|
|
location: '东京',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1503899036084-c55cdd92da26?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '东京,一座永不入睡的城市!从繁华的涉谷到宁静的浅草寺,感受这里的无限活力。分享我在东京的精彩旅程! #东京探险 #日本文化',
|
|
|
|
|
|
|
|
likes: 256,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 3,
|
|
|
|
|
|
|
|
user: '小李',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1552058544-f2b08422138a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
|
|
|
|
|
|
|
location: '纽约',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '纽约,梦想之城!从中央公园到百老汇,每一刻都充满可能。在这里,平凡人也能创造非凡故事。分享我的纽约冒险! #纽约生活 #追梦人生',
|
|
|
|
|
|
|
|
likes: 412,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 4,
|
|
|
|
|
|
|
|
user: '小张',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
|
|
|
|
|
|
|
location: '伦敦',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1513635269975-59663e0ac1ad?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '伦敦,历史与现代的完美融合!从大本钟到泰特现代美术馆,每一步都是文化的碰撞。分享我在伦敦的奇妙时光! #伦敦印象 #英伦风情',
|
|
|
|
|
|
|
|
likes: 189,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 5,
|
|
|
|
|
|
|
|
user: '小王',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
|
|
|
|
|
|
|
location: '悉尼',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1506973035872-a4ec16b8e8d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '悉尼,阳光与海滩的天堂!从歌剧院到邦迪海滩,处处洋溢着欢乐。分享我在这里的阳光假期! #悉尼生活 #澳洲之美',
|
|
|
|
|
|
|
|
likes: 275,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 6,
|
|
|
|
|
|
|
|
user: '小陈',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
|
|
|
|
|
|
|
location: '罗马',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1552832230-c0197dd311b5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '罗马,永恒之城!从斗兽场到梵蒂冈,每一处都是历史的见证。分享我在这座古城的深度探索! #罗马假日 #意大利风情',
|
|
|
|
|
|
|
|
likes: 301,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
id: 7,
|
|
|
|
|
|
|
|
user: '小林',
|
|
|
|
|
|
|
|
avatar: 'https://images.unsplash.com/photo-1547425260-76bcadfb4f2c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80',
|
|
|
|
|
|
|
|
location: '巴塞罗那',
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1583422409516-2895a77efded?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80',
|
|
|
|
|
|
|
|
content: '巴塞罗那,一座充满激情的城市!高迪的建筑让人惊叹,地中海的阳光温暖人心。分享我在这里的精彩冒险! #巴塞罗那风情 #建筑奇迹',
|
|
|
|
|
|
|
|
likes: 233,
|
|
|
|
|
|
|
|
comments: [],
|
|
|
|
|
|
|
|
isLiked: false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
// 创建一个状态来存储新评论
|
|
|
|
|
|
|
|
const [newComment, setNewComment] = useState('')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 组件挂载时,从userDatabase加载用户数据
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
setUsers(userDatabase);
|
|
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理盲盒匹配的函数
|
|
|
|
|
|
|
|
const 处理盲盒匹配 = () => {
|
|
|
|
|
|
|
|
console.log('开始盲盒匹配');
|
|
|
|
|
|
|
|
// 设置匹配状态为true,开始匹配进度为0
|
|
|
|
|
|
|
|
setIsMatching(true);
|
|
|
|
|
|
|
|
setMatchingProgress(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建一个定时器,每200毫秒增加10%的进度,直到100%
|
|
|
|
|
|
|
|
const intervalId = setInterval(() => {
|
|
|
|
|
|
|
|
setMatchingProgress(prev => {
|
|
|
|
|
|
|
|
if (prev >= 100) {
|
|
|
|
|
|
|
|
clearInterval(intervalId);
|
|
|
|
|
|
|
|
return 100;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return prev + 10;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2秒后执行实际的匹配逻辑
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
// 构造当前用户对象
|
|
|
|
|
|
|
|
const currentUser = {
|
|
|
|
|
|
|
|
budget: Number(预算),
|
|
|
|
|
|
|
|
destination: 目的地,
|
|
|
|
|
|
|
|
travelDate: new Date(出行日期),
|
|
|
|
|
|
|
|
interests: ['旅行', '摄影', '美食'], // 这里应该从用户资料中获取
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
console.log('当前用户:', currentUser);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对所有用户进行匹配计算
|
|
|
|
|
|
|
|
const matchedUsers = users.map(user => {
|
|
|
|
|
|
|
|
const matchUser = {
|
|
|
|
|
|
|
|
...user,
|
|
|
|
|
|
|
|
budget: Number(user.budget),
|
|
|
|
|
|
|
|
destination: user.location,
|
|
|
|
|
|
|
|
travelDate: new Date(user.travelDate),
|
|
|
|
|
|
|
|
interests: user.interests || []
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
console.log('匹配用户:', matchUser);
|
|
|
|
|
|
|
|
// 计算相似度
|
|
|
|
|
|
|
|
const similarity = calculateSimilarity(currentUser, matchUser);
|
|
|
|
|
|
|
|
console.log('相似度分数:', similarity);
|
|
|
|
|
|
|
|
return { ...user, similarity };
|
|
|
|
|
|
|
|
}).sort((a, b) => b.similarity - a.similarity); // 按相似度降序排序
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('匹配结果:', matchedUsers);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置匹配结果
|
|
|
|
|
|
|
|
if (matchedUsers.length > 0) {
|
|
|
|
|
|
|
|
setMatchResult(matchedUsers[0]);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
setMatchResult(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 结束匹配过程
|
|
|
|
|
|
|
|
setIsMatching(false);
|
|
|
|
|
|
|
|
clearInterval(intervalId);
|
|
|
|
|
|
|
|
setMatchingProgress(100);
|
|
|
|
|
|
|
|
}, 2000);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理点赞功能的函数
|
|
|
|
|
|
|
|
const handleLike = (postId: number) => {
|
|
|
|
|
|
|
|
setPosts(posts.map(post => {
|
|
|
|
|
|
|
|
if (post.id === postId) {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...post,
|
|
|
|
|
|
|
|
// 如果已经点赞,则减少点赞数;否则增加点赞数
|
|
|
|
|
|
|
|
likes: post.isLiked ? post.likes - 1 : post.likes + 1,
|
|
|
|
|
|
|
|
// 切换点赞状态
|
|
|
|
|
|
|
|
isLiked: !post.isLiked
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return post
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理评论功能的函数
|
|
|
|
|
|
|
|
const handleComment = (postId: number) => {
|
|
|
|
|
|
|
|
// 检查新评论是否为空(去掉首尾空格后)
|
|
|
|
|
|
|
|
if (newComment.trim()) {
|
|
|
|
|
|
|
|
// 更新帖子列表
|
|
|
|
|
|
|
|
setPosts(posts.map(post => {
|
|
|
|
|
|
|
|
// 找到要评论的帖子
|
|
|
|
|
|
|
|
if (post.id === postId) {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...post,
|
|
|
|
|
|
|
|
// 将新评论添加到评论列表中
|
|
|
|
|
|
|
|
comments: [...post.comments, newComment]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 其他帖子保持不变
|
|
|
|
|
|
|
|
return post
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
// 清空评论输入框
|
|
|
|
|
|
|
|
setNewComment('')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理分享功能的函数
|
|
|
|
|
|
|
|
const handleShare = (postId: number) => {
|
|
|
|
|
|
|
|
// 构造帖子的URL
|
|
|
|
|
|
|
|
const url = `https://yourtravelwebsite.com/post/${postId}`
|
|
|
|
|
|
|
|
// 将URL复制到剪贴板
|
|
|
|
|
|
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
|
|
|
|
|
|
// 复制成功后显示提示
|
|
|
|
|
|
|
|
alert('链接已复制到剪贴板!')
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const events = [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '徒步长城',
|
|
|
|
|
|
|
|
description: '体验中国最著名的古代建筑,欣赏壮丽的风景,结识志同道合的徒步爱好者。',
|
|
|
|
|
|
|
|
date: '2023年10月15日',
|
|
|
|
|
|
|
|
duration: '1天',
|
|
|
|
|
|
|
|
difficulty: '中等',
|
|
|
|
|
|
|
|
included: ['专业导游', '午餐', '往返交通'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1508804185872-d7badad00f7d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '樱花季摄影',
|
|
|
|
|
|
|
|
description: '在美丽的樱花季节,和其他摄影爱好者一起捕捉最美的樱花景象,提升您的摄影技巧。',
|
|
|
|
|
|
|
|
date: '2024年3月20日',
|
|
|
|
|
|
|
|
duration: '半天',
|
|
|
|
|
|
|
|
difficulty: '简单',
|
|
|
|
|
|
|
|
included: ['摄影指导', '下午茶', '照片后期工作坊'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1522383225653-ed111181a951?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2076&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '威尼斯狂欢节',
|
|
|
|
|
|
|
|
description: '参加世界闻名的威尼斯狂欢节,体验独特的面具文化和浪漫的水城氛围。',
|
|
|
|
|
|
|
|
date: '2024年2月10日',
|
|
|
|
|
|
|
|
duration: '3天',
|
|
|
|
|
|
|
|
difficulty: '简单',
|
|
|
|
|
|
|
|
included: ['面具工作坊', '狂欢节游行门票', '威尼斯特色美食品尝'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1518730518541-d0f4ea9dfab9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '亚马逊丛林探险',
|
|
|
|
|
|
|
|
description: '深入世界最大的热带雨林,探索丰富的生物多样性,体验原始部落文化。',
|
|
|
|
|
|
|
|
date: '2023年11月5日',
|
|
|
|
|
|
|
|
duration: '5天',
|
|
|
|
|
|
|
|
difficulty: '高',
|
|
|
|
|
|
|
|
included: ['专业向导', '丛林住宿', '野生动物观察', '部落文化体验'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1516426122078-c23e76319801?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '北极光观测之旅',
|
|
|
|
|
|
|
|
description: '前往芬兰拉普兰,在极地玻璃屋中观赏神奇的北极光,体验独特的萨米文化。',
|
|
|
|
|
|
|
|
date: '2024年1月15日',
|
|
|
|
|
|
|
|
duration: '4天',
|
|
|
|
|
|
|
|
difficulty: '中等',
|
|
|
|
|
|
|
|
included: ['极光观测', '哈士奇雪橇', '冰钓体验', '萨米文化之夜'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1531366936337-7c912a4589a7?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '日本寿司制作课程',
|
|
|
|
|
|
|
|
description: '在东京学习正宗的寿司制作技巧,深入了解日本饮食文化,品尝自己亲手制作的寿司。',
|
|
|
|
|
|
|
|
date: '2023年9月25日',
|
|
|
|
|
|
|
|
duration: '1天',
|
|
|
|
|
|
|
|
difficulty: '中等',
|
|
|
|
|
|
|
|
included: ['专业寿司师指导', '所有食材和工具', '寿司午餐', '参观筑地市场'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1553621042-f6e147245754?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '马丘比丘徒步之旅',
|
|
|
|
|
|
|
|
description: '沿着古老的印加小道徒步,探索神秘的马丘比丘遗址,感受安第斯山脉的壮美。',
|
|
|
|
|
|
|
|
date: '2024年4月10日',
|
|
|
|
|
|
|
|
duration: '4天',
|
|
|
|
|
|
|
|
difficulty: '高',
|
|
|
|
|
|
|
|
included: ['专业向导', '露营装备', '餐食', '马丘比丘门票'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1526392060635-9d6019884377?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const inspirations = [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '原始森林探索',
|
|
|
|
|
|
|
|
description: '深入地球上最古老的原始森林,探索生物多样性的奇迹。在茂密的树冠下徒步,观察珍稀动植物,感受大自然的神奇力量。',
|
|
|
|
|
|
|
|
activities: ['丛林徒步', '野生动物观察', '生态摄影', '夜间探险'],
|
|
|
|
|
|
|
|
destinations: ['亚马逊雨林', '刚果盆地', '大兴安岭', '婆罗洲'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1511497584788-876760111969?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '极地冰川探险',
|
|
|
|
|
|
|
|
description: '前往地球的极端地带,探索壮观的冰川世界。体验极昼或极夜,观察北极熊和企鹅,感受冰雪世界的震撼美景。',
|
|
|
|
|
|
|
|
activities: ['冰川徒步', '北极光观测', '冰上皮划艇', '极地野生动物观察'],
|
|
|
|
|
|
|
|
destinations: ['南极半岛', '格陵兰', '斯瓦尔巴群岛', '巴塔哥尼亚冰原'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1494564605686-2e931f77a8e2?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '火山地质探索',
|
|
|
|
|
|
|
|
description: '探索地球上最活跃的火山地带,感受大自然的原始力量。观察独特的火山地貌,体验地热温泉,了解火山对生态系统的影响。',
|
|
|
|
|
|
|
|
activities: ['火山徒步', '温泉浸泡', '地质考察', '熔岩洞穴探索'],
|
|
|
|
|
|
|
|
destinations: ['夏威夷火山国家公园', '冰岛', '印度尼西亚默拉皮火山', '意大利埃特纳火山'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1462332420958-a05d1e002413?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '沙漠探险之旅',
|
|
|
|
|
|
|
|
description: '在广袤的沙漠中体验极致的宁静与壮美。欣赏绚丽的日出日落,探索神秘的绿洲,感受沙漠文化的独特魅力。',
|
|
|
|
|
|
|
|
activities: ['沙漠徒步', '骆驼骑行', '沙丘冲浪', '星空观测'],
|
|
|
|
|
|
|
|
destinations: ['撒哈拉沙漠', '纳米比亚沙漠', '戈壁沙漠', '澳大利亚辛普森沙漠'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1682686580391-615b1e32be1d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '海底世界探索',
|
|
|
|
|
|
|
|
description: '潜入神秘的海底世界,探索丰富多彩的海洋生态系统。观察五彩斑斓的珊瑚礁,与海洋生物亲密接触,感受海洋的魅力与重要性。',
|
|
|
|
|
|
|
|
activities: ['深潜', '浮潜', '珊瑚礁观察', '海洋生物研究'],
|
|
|
|
|
|
|
|
destinations: ['大堡礁', '帕劳', '马尔代夫', '加拉帕戈斯群岛'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1682687982501-1e58ab814714?ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '高山探险挑战',
|
|
|
|
|
|
|
|
description: '挑战世界上最高的山峰,体验登山的刺激与成就感。欣赏壮丽的高山景色,了解高山生态系统,感受大自然的宏伟。',
|
|
|
|
|
|
|
|
activities: ['高山攀登', '冰川徒步', '高山摄影', '野外生存训练'],
|
|
|
|
|
|
|
|
destinations: ['珠穆朗玛峰', '乞力马扎罗山', '阿空加瓜山', '勃朗峰'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1464278533981-50106e6176b1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2074&q=80'
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
title: '国家公园探索',
|
|
|
|
|
|
|
|
description: '探索世界著名的国家公园,感受大自然的鬼斧神工。观察多样的野生动物,徒步壮丽的山川,体验自然保护的重要性。',
|
|
|
|
|
|
|
|
activities: ['野生动物观察', '徒步旅行', '露营', '风景摄影'],
|
|
|
|
|
|
|
|
destinations: ['黄石国家公园', '塞伦盖蒂国家公园', '托斯卡纳群岛国家公园', '吉尔吉斯斯坦天山国家公园'],
|
|
|
|
|
|
|
|
image: 'https://images.unsplash.com/photo-1472396961693-142e6e269027?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2106&q=80'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
<div className={gradientBackground}>
|
|
|
|
|
|
|
|
<header className="bg-white shadow sticky top-0 z-50">
|
|
|
|
|
|
|
|
{showProfile && <个人资料 onClose={() => setShowProfile(false)} />}
|
|
|
|
|
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
|
|
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
|
|
|
<h1 className="text-3xl font-bold text-primary">旅游交友网</h1>
|
|
|
|
|
|
|
|
<nav className="hidden md:flex space-x-4">
|
|
|
|
|
|
|
|
<Button variant="default" className="bg-black bg-opacity-50 text-white hover:bg-opacity-100">发现</Button>
|
|
|
|
|
|
|
|
<Button variant="default" className="bg-black bg-opacity-50 text-white hover:bg-opacity-100">目的地</Button>
|
|
|
|
|
|
|
|
<Button variant="default" className="bg-black bg-opacity-50 text-white hover:bg-opacity-100">社区</Button>
|
|
|
|
|
|
|
|
<Button variant="default" className="bg-black bg-opacity-50 text-white hover:bg-opacity-100">活动</Button>
|
|
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
|
|
|
<Button variant="ghost" size="icon"><Bell /></Button>
|
|
|
|
|
|
|
|
<DropdownMenu>
|
|
|
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
|
|
|
<Button variant="ghost" size="icon"><User /></Button>
|
|
|
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
|
|
|
<DropdownMenuContent align="end">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<DropdownMenuItem onSelect={() => setShowProfile(true)}>个人资料</DropdownMenuItem>
|
|
|
|
|
|
|
|
<DropdownMenuItem>我的行程</DropdownMenuItem>
|
|
|
|
|
|
|
|
<DropdownMenuItem>设置</DropdownMenuItem>
|
|
|
|
|
|
|
|
<DropdownMenuItem>退出登录</DropdownMenuItem>
|
|
|
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
|
|
|
<Button variant="ghost" size="icon" className="md:hidden"><Menu /></Button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
|
|
|
|
|
|
<section className="mb-12 bg-gradient-to-r from-blue-500 to-purple-600 rounded-xl p-8 text-white relative overflow-hidden">
|
|
|
|
|
|
|
|
<img
|
|
|
|
|
|
|
|
src="https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2021&q=80"
|
|
|
|
|
|
|
|
className="absolute inset-0 w-full h-full object-cover opacity-50"
|
|
|
|
|
|
|
|
alt="背景图片"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div className="relative z-10">
|
|
|
|
|
|
|
|
<h2 className="text-4xl font-bold mb-4">探索世界,结识好友</h2>
|
|
|
|
|
|
|
|
<p className="text-xl mb-6">使用我们的旅行交友盲盒,开启你的下一次冒险!</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
|
|
placeholder="输入目的地"
|
|
|
|
|
|
|
|
value={目的地}
|
|
|
|
|
|
|
|
onChange={(e) => set目的地(e.target.value)}
|
|
|
|
|
|
|
|
className="bg-white text-black"
|
|
|
|
|
|
|
|
icon={<MapPin className="text-gray-400" />}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
|
|
value={出行日期}
|
|
|
|
|
|
|
|
onChange={(e) => set出行日期(e.target.value)}
|
|
|
|
|
|
|
|
className="bg-white text-black"
|
|
|
|
|
|
|
|
icon={<Calendar className="text-gray-400" />}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
|
|
placeholder="输入预算(元)"
|
|
|
|
|
|
|
|
value={预算}
|
|
|
|
|
|
|
|
onChange={(e) => set预算(e.target.value)}
|
|
|
|
|
|
|
|
className="bg-white text-black"
|
|
|
|
|
|
|
|
icon={<DollarSign className="text-gray-400" />}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<Dialog>
|
|
|
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
|
|
|
<Button className="bg-yellow-400 text-black hover:bg-yellow-300 w-full">
|
|
|
|
|
|
|
|
<Gift className="mr-2" />
|
|
|
|
|
|
|
|
开启旅行交友盲盒
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
|
|
|
<DialogContent className="sm:max-w-[600px]">
|
|
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
|
|
<DialogTitle>旅行交友盲盒</DialogTitle>
|
|
|
|
|
|
|
|
<DialogDescription>
|
|
|
|
|
|
|
|
{isMatching ? "正在为你匹配志同道合的旅伴,请稍候..." : "准备好开始新的冒险了吗?"}
|
|
|
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
<div className="flex justify-center items-center min-h-[400px]">
|
|
|
|
|
|
|
|
{isMatching ? (
|
|
|
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
|
|
|
<Loader2 className="w-16 h-16 text-blue-500 animate-spin mx-auto mb-4" />
|
|
|
|
|
|
|
|
<Progress value={matchingProgress} className="w-64 mx-auto mb-2" />
|
|
|
|
|
|
|
|
<p>正在寻找最佳匹配...</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
) : matchResult ? (
|
|
|
|
|
|
|
|
<div className="w-full">
|
|
|
|
|
|
|
|
<div className="text-center mb-6">
|
|
|
|
|
|
|
|
<Avatar className="w-32 h-32 mx-auto mb-4">
|
|
|
|
|
|
|
|
<AvatarImage src={matchResult.avatar} alt={matchResult.name} />
|
|
|
|
|
|
|
|
<AvatarFallback>{matchResult.name[0]}</AvatarFallback>
|
|
|
|
|
|
|
|
</Avatar>
|
|
|
|
|
|
|
|
<h3 className="text-2xl font-semibold mb-2">{matchResult.name}, {matchResult.age}</h3>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-2">
|
|
|
|
|
|
|
|
<MapPin className="inline-block mr-1" size={16} />
|
|
|
|
|
|
|
|
{matchResult.location}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-4">
|
|
|
|
|
|
|
|
<DollarSign className="inline-block mr-1" size={16} />
|
|
|
|
|
|
|
|
预算: {matchResult.budget}元
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<div className="flex justify-center space-x-2 mb-4 flex-wrap">
|
|
|
|
|
|
|
|
{matchResult.interests.map((interest, index) => (
|
|
|
|
|
|
|
|
<Badge key={index} variant="secondary" className="mb-2">{interest}</Badge>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">个人简介</h4>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-4">{matchResult.bio}</p>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">职业与旅行风格</h4>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-1">
|
|
|
|
|
|
|
|
<Briefcase className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.occupation}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-4">
|
|
|
|
|
|
|
|
<Plane className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.travelStyle}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">语言</h4>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-4">
|
|
|
|
|
|
|
|
<Languages className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.languages.join(', ')}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">联系方式</h4>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-1">
|
|
|
|
|
|
|
|
<Mail className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.email}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-1">
|
|
|
|
|
|
|
|
<Phone className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.phone}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-1">
|
|
|
|
|
|
|
|
<Instagram className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.socialMedia.instagram}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<p className="text-gray-600 mb-4">
|
|
|
|
|
|
|
|
<Globe className="inline-block mr-2" size={16} />
|
|
|
|
|
|
|
|
{matchResult.socialMedia.website}
|
|
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">最近旅行</h4>
|
|
|
|
|
|
|
|
<div className="flex flex-wrap mb-4">
|
|
|
|
|
|
|
|
{matchResult.recentTrips.map((trip, index) => (
|
|
|
|
|
|
|
|
<Badge key={index} variant="outline" className="mr-2 mb-2">{trip}</Badge>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">即将出发</h4>
|
|
|
|
|
|
|
|
<div className="flex flex-wrap">
|
|
|
|
|
|
|
|
{matchResult.upcomingTrips.map((trip, index) => (
|
|
|
|
|
|
|
|
<Badge key={index} variant="outline" className="mr-2 mb-2">{trip}</Badge>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="mt-6">
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">旅伴评价</h4>
|
|
|
|
|
|
|
|
{matchResult.reviews.map((review, index) => (
|
|
|
|
|
|
|
|
<div key={index} className="bg-gray-100 rounded-lg p-3 mb-2">
|
|
|
|
|
|
|
|
<p className="font-semibold">{review.author} <span className="text-yellow-500">{'★'.repeat(review.rating)}</span></p>
|
|
|
|
|
|
|
|
<p className="text-gray-600">{review.content}</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
) : (
|
|
|
|
|
|
|
|
<Gift className="w-24 h-24 text-yellow-400" />
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{!isMatching && (
|
|
|
|
|
|
|
|
<Button onClick={处理盲盒匹配} disabled={isMatching || !目的地 || !出行日期 || !预算} className="w-full mt-4">
|
|
|
|
|
|
|
|
{matchResult ? "重新匹配" : "开始匹配"}
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<CarouselSection
|
|
|
|
|
|
|
|
title="社区动态"
|
|
|
|
|
|
|
|
items={posts}
|
|
|
|
|
|
|
|
renderItem={(post) => (
|
|
|
|
|
|
|
|
<Card key={post.id}>
|
|
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
|
|
|
<Avatar>
|
|
|
|
|
|
|
|
<AvatarImage src={post.avatar} alt={`${post.user}头像`} />
|
|
|
|
|
|
|
|
<AvatarFallback>{post.user[0]}</AvatarFallback>
|
|
|
|
|
|
|
|
</Avatar>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h3 className="font-semibold">{post.user}</h3>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-500">3小时前</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
|
|
<CardContent>
|
|
|
|
|
|
|
|
<p className="mb-2">{post.content}</p>
|
|
|
|
|
|
|
|
<img src={post.image} alt="旅行照片" className="w-full h-48 object-cover rounded-lg mb-2" />
|
|
|
|
|
|
|
|
<div className="flex space-x-2">
|
|
|
|
|
|
|
|
{post.content.split('#').slice(1).map((tag, index) => (
|
|
|
|
|
|
|
|
<Badge key={index} variant="secondary">#{tag.trim()}</Badge>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
|
|
<CardFooter className="flex justify-between">
|
|
|
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={() => handleLike(post.id)}>
|
|
|
|
|
|
|
|
<Heart className={`mr-2 ${post.isLiked ? 'fill-red-500 text-red-500' : ''}`} />
|
|
|
|
|
|
|
|
{post.likes}
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<Dialog>
|
|
|
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
|
|
|
<Button variant="ghost" size="sm">
|
|
|
|
|
|
|
|
<MessageCircle className="mr-2" />
|
|
|
|
|
|
|
|
{post.comments.length}
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
|
|
|
<DialogContent>
|
|
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
|
|
<DialogTitle>评论</DialogTitle>
|
|
|
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
<div className="max-h-[300px] overflow-y-auto">
|
|
|
|
|
|
|
|
{post.comments.map((comment, index) => (
|
|
|
|
|
|
|
|
<div key={index} className="mb-2 p-2 bg-gray-100 rounded">
|
|
|
|
|
|
|
|
<p>{comment}</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
|
|
|
<Textarea
|
|
|
|
|
|
|
|
placeholder="添加评论..."
|
|
|
|
|
|
|
|
value={newComment}
|
|
|
|
|
|
|
|
onChange={(e) => setNewComment(e.target.value)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<Button onClick={() => handleComment(post.id)} className="mt-2">
|
|
|
|
|
|
|
|
发表评论
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={() => handleShare(post.id)}>
|
|
|
|
|
|
|
|
<Share2 className="mr-2" />分享
|
|
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
</CardFooter>
|
|
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<CarouselSection
|
|
|
|
|
|
|
|
title="即将开始的旅行活动"
|
|
|
|
|
|
|
|
items={events}
|
|
|
|
|
|
|
|
renderItem={(event) => (
|
|
|
|
|
|
|
|
<Card key={event.title} className="hover:shadow-lg transition-shadow duration-300">
|
|
|
|
|
|
|
|
<CardContent className="p-0">
|
|
|
|
|
|
|
|
<img src={event.image} alt={event.title} className="w-full h-40 object-cover" />
|
|
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
|
|
<h3 className="font-semibold text-lg mb-2">{event.title}</h3>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-600 mb-2">日期:{event.date}</p>
|
|
|
|
|
|
|
|
<Dialog>
|
|
|
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
|
|
|
<Button variant="outline" size="sm">了解详情</Button>
|
|
|
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
|
|
|
<DialogContent>
|
|
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
|
|
<DialogTitle>{event.title}</DialogTitle>
|
|
|
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
|
|
|
<img src={event.image} alt={event.title} className="w-full h-48 object-cover rounded-lg mb-4" />
|
|
|
|
|
|
|
|
<p className="text-gray-700 mb-2">{event.description}</p>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-600 mb-1"><strong>日期:</strong>{event.date}</p>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-600 mb-1"><strong>持续时间:</strong>{event.duration}</p>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-600 mb-1"><strong>难度:</strong>{event.difficulty}</p>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mt-4 mb-2">包含内容:</h4>
|
|
|
|
|
|
|
|
<ul className="list-disc list-inside text-sm text-gray-600">
|
|
|
|
|
|
|
|
{event.included.map((item, i) => (
|
|
|
|
|
|
|
|
<li key={i}>{item}</li>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<CarouselSection
|
|
|
|
|
|
|
|
title="旅行灵感"
|
|
|
|
|
|
|
|
items={inspirations}
|
|
|
|
|
|
|
|
renderItem={(inspiration) => (
|
|
|
|
|
|
|
|
<Card key={inspiration.title} className="hover:shadow-lg transition-shadow duration-300">
|
|
|
|
|
|
|
|
<CardContent className="p-0">
|
|
|
|
|
|
|
|
<img src={inspiration.image} alt={inspiration.title} className="w-full h-40 object-cover" />
|
|
|
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
|
|
|
<h3 className="font-semibold text-lg mb-2">{inspiration.title}</h3>
|
|
|
|
|
|
|
|
<p className="text-sm text-gray-600 mb-2">{inspiration.description.substring(0, 100)}...</p>
|
|
|
|
|
|
|
|
<Dialog>
|
|
|
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
|
|
|
<Button variant="outline" size="sm">探索更多</Button>
|
|
|
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
|
|
|
<DialogContent className="max-w-3xl">
|
|
|
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
|
|
|
<DialogTitle>{inspiration.title}</DialogTitle>
|
|
|
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
|
|
|
<img src={inspiration.image} alt={inspiration.title} className="w-full h-64 object-cover rounded-lg mb-4" />
|
|
|
|
|
|
|
|
<p className="text-gray-700 mb-4">{inspiration.description}</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">推荐活动:</h4>
|
|
|
|
|
|
|
|
<ul className="list-disc list-inside text-sm text-gray-600">
|
|
|
|
|
|
|
|
{inspiration.activities.map((activity, i) => (
|
|
|
|
|
|
|
|
<li key={i}>{activity}</li>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h4 className="font-semibold mb-2">热门目的地:</h4>
|
|
|
|
|
|
|
|
<ul className="list-disc list-inside text-sm text-gray-600">
|
|
|
|
|
|
|
|
{inspiration.destinations.map((destination, i) => (
|
|
|
|
|
|
|
|
<li key={i}>{destination}</li>
|
|
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</DialogContent>
|
|
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-4 text-black">旅行小贴士</h2>
|
|
|
|
|
|
|
|
<div className="bg-white p-6 rounded-lg shadow">
|
|
|
|
|
|
|
|
<ul className="list-disc list-inside space-y-2 text-black">
|
|
|
|
|
|
|
|
<li>记得购买旅行保险,以应对突发情况。</li>
|
|
|
|
|
|
|
|
<li>提前了解目的地的文化习俗,尊重当地传统。</li>
|
|
|
|
|
|
|
|
<li>准备便携式充电器,保持设备电量充足。</li>
|
|
|
|
|
|
|
|
<li>使用我们的App,随时与新朋友保持联系。</li>
|
|
|
|
|
|
|
|
<li>尝试当地特色美食,但注意饮食卫生。</li>
|
|
|
|
|
|
|
|
<li>保持开放心态,拥抱新的文化和体验。</li>
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<footer className="bg-gray-800 text-white mt-12">
|
|
|
|
|
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4">关于我们</h3>
|
|
|
|
|
|
|
|
<p className="text-sm">旅游交友网致力于为旅行者提供独特的社交体验,让每次旅程都充满惊喜和温暖。</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4">快速链接</h3>
|
|
|
|
|
|
|
|
<ul className="space-y-2">
|
|
|
|
|
|
|
|
<li><a href="#" className="text-sm hover:underline">使用条款</a></li>
|
|
|
|
|
|
|
|
<li><a href="#" className="text-sm hover:underline">隐私政策</a></li>
|
|
|
|
|
|
|
|
<li><a href="#" className="text-sm hover:underline">常见问题</a></li>
|
|
|
|
|
|
|
|
<li><a href="#" className="text-sm hover:underline">联系我们</a></li>
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4">关注我们</h3>
|
|
|
|
|
|
|
|
<div className="flex space-x-4">
|
|
|
|
|
|
|
|
<a href="#" className="text-white hover:text-gray-300"><svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path fillRule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" /></svg></a>
|
|
|
|
|
|
|
|
<a href="#" className="text-white hover:text-gray-300"><svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path fillRule="evenodd" d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" clipRule="evenodd" /></svg></a>
|
|
|
|
|
|
|
|
<a href="#" className="text-white hover:text-gray-300"><svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" /></svg></a>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold mb-4">订阅我们</h3>
|
|
|
|
|
|
|
|
<p className="text-sm mb-2">获取最新的旅行灵感和独家优惠</p>
|
|
|
|
|
|
|
|
<form className="flex">
|
|
|
|
|
|
|
|
<Input type="email" placeholder="您的邮箱" className="rounded-r-none" />
|
|
|
|
|
|
|
|
<Button type="submit" className="rounded-l-none">订阅</Button>
|
|
|
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="mt-8 pt-8 border-t border-gray-700 text-center">
|
|
|
|
|
|
|
|
<p className="text-sm">© 2023 旅游交友网. 保留所有权利。</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|