Files
smalltown/app/house/[id]/page.tsx

210 lines
6.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect } from "react";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
interface House {
id: string;
owner: string;
title: string;
description: string;
price: number;
district: string;
address: string;
phone: string;
images: string[];
createdAt: string;
}
export default function HouseDetail() {
const params = useParams();
const router = useRouter();
const [house, setHouse] = useState<House | null>(null);
const [loading, setLoading] = useState(true);
const [currentImage, setCurrentImage] = useState(0);
const [showContact, setShowContact] = useState(false);
useEffect(() => {
fetchHouse();
}, []);
async function fetchHouse() {
try {
const res = await fetch(`/api/houses/${params.id}`);
const data = await res.json();
if (data.house) {
setHouse(data.house);
}
} catch (error) {
console.error("获取房屋详情失败", error);
} finally {
setLoading(false);
}
}
function formatDate(dateStr: string) {
return new Date(dateStr).toLocaleDateString("zh-CN");
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<div className="text-4xl mb-2 animate-bounce">🏠</div>
<p className="text-gray-500">...</p>
</div>
</div>
);
}
if (!house) {
return (
<div className="min-h-screen flex flex-col items-center justify-center">
<div className="text-6xl mb-4">😢</div>
<p className="text-gray-500 mb-4"></p>
<Link href="/" className="px-4 py-2 bg-orange-500 text-white rounded-lg">
</Link>
</div>
);
}
const images = house.images && house.images.length > 0 ? house.images : [];
return (
<div className="min-h-screen bg-gray-50 pb-32">
<header className="bg-white sticky top-0 z-20 px-4 py-3 flex items-center gap-4 shadow-sm">
<button onClick={() => router.back()} className="text-2xl">
</button>
<h1 className="font-semibold text-gray-900 truncate flex-1"></h1>
</header>
{images.length > 0 ? (
<div className="relative">
<div className="overflow-x-auto snap-x snap-mandatory flex">
{images.map((img, index) => (
<img
key={index}
src={img}
alt={`${house.title} ${index + 1}`}
className="w-full h-72 object-cover flex-shrink-0 snap-center"
/>
))}
</div>
{images.length > 1 && (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2">
{images.map((_, index) => (
<button
key={index}
onClick={() => setCurrentImage(index)}
className={`w-2 h-2 rounded-full ${
index === currentImage ? "bg-white" : "bg-white/50"
}`}
/>
))}
</div>
)}
</div>
) : (
<div className="w-full h-72 bg-gradient-to-br from-orange-100 to-orange-200 flex items-center justify-center text-8xl">
🏠
</div>
)}
<div className="px-4 py-4">
<div className="bg-white rounded-xl p-4 mb-4 shadow-sm">
<div className="flex items-start justify-between mb-3">
<div>
<h2 className="text-xl font-bold text-gray-900 mb-1">{house.title}</h2>
<span className="inline-block bg-orange-100 text-orange-600 text-xs px-2 py-1 rounded">
{house.district}
</span>
</div>
<div className="text-right">
<span className="text-2xl font-bold text-orange-500">¥{house.price}</span>
<span className="text-gray-400 text-sm">/</span>
</div>
</div>
<div className="flex items-center text-gray-500 text-sm mb-3">
<span className="text-lg mr-2">📍</span>
<span>{house.address}</span>
</div>
<div className="flex items-center text-gray-500 text-sm">
<span className="text-lg mr-2">👤</span>
<span>{house.owner}</span>
</div>
</div>
{house.description && (
<div className="bg-white rounded-xl p-4 mb-4 shadow-sm">
<h3 className="font-semibold text-gray-900 mb-2"></h3>
<p className="text-gray-600 text-sm leading-relaxed whitespace-pre-wrap">
{house.description}
</p>
</div>
)}
<div className="bg-white rounded-xl p-4 shadow-sm">
<h3 className="font-semibold text-gray-900 mb-3"></h3>
<p className="text-gray-500 text-sm">{formatDate(house.createdAt)}</p>
</div>
</div>
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-4 max-w-lg mx-auto">
<button
onClick={() => setShowContact(true)}
className="w-full py-4 bg-gradient-to-r from-orange-500 to-orange-600 text-white font-semibold rounded-xl shadow-lg active:scale-[0.98] transition-transform"
>
📞
</button>
</div>
{showContact && (
<div className="fixed inset-0 bg-black/50 z-50 flex items-end">
<div className="bg-white w-full rounded-t-3xl p-6 animate-slide-up">
<div className="w-12 h-1 bg-gray-300 rounded-full mx-auto mb-6"></div>
<h3 className="text-xl font-bold text-gray-900 mb-4 text-center"></h3>
<div className="bg-orange-50 rounded-xl p-6 text-center mb-6">
<div className="text-4xl mb-2">📱</div>
<p className="text-3xl font-bold text-orange-500 tracking-wider">{house.phone}</p>
<p className="text-gray-500 text-sm mt-2"></p>
</div>
<div className="flex gap-3">
<a
href={`tel:${house.phone}`}
className="flex-1 py-4 bg-orange-500 text-white font-semibold rounded-xl text-center"
>
</a>
<button
onClick={() => setShowContact(false)}
className="flex-1 py-4 bg-gray-100 text-gray-700 font-semibold rounded-xl"
>
</button>
</div>
</div>
</div>
)}
<style jsx>{`
@keyframes slide-up {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.animate-slide-up {
animation: slide-up 0.3s ease-out;
}
`}</style>
</div>
);
}