如何在NextJS应用中创建动态影院选座布局
实现NextJS影院动态选座布局方案
1. 定义结构化的座位数据
要支持不同影院的动态布局,首先需要后端返回结构化的座位数据,每个影厅的座位可以用包含行信息的数组表示,每个座位包含状态标识:
// 示例座位数据结构(可从API获取) const sampleTheaterSeats = [ { row: "A", seats: [{ id: "A1", status: "available" }, { id: "A2", status: "sold" }, { id: "A3", status: "available" }] }, { row: "B", seats: [{ id: "B1", status: "sold" }, { id: "B2", status: "available" }, { id: "B3", status: "available" }, { id: "B4", status: "sold" }] }, { row: "C", seats: Array.from({ length: 5 }, (_, i) => ({ id: `C${i+1}`, status: Math.random() > 0.3 ? "available" : "sold" })) } ];
status字段用sold(已售出)/available(可预订)区分状态
2. 拆分组件实现复用
基于NextJS的组件化思想,拆分出三个核心组件:
单个座位组件(Seat.jsx)
负责渲染单个座位,根据状态应用样式:
export default function Seat({ seat, onSelect }) { const seatClasses = seat.status === "sold" ? "bg-yellow-500 cursor-not-allowed" : "bg-white border-gray-300 hover:bg-gray-200 cursor-pointer"; return ( <button className={`w-8 h-8 rounded-md mx-1 ${seatClasses}`} disabled={seat.status === "sold"} onClick={() => seat.status === "available" && onSelect(seat.id)} > {seat.id} </button> ); }
座位行组件(SeatRow.jsx)
渲染整行座位,包含行号标识:
import Seat from "./Seat"; export default function SeatRow({ row, onSelect }) { return ( <div className="flex items-center mb-2"> <span className="w-8 text-right mr-2 font-medium">{row.row}</span> <div className="flex"> {row.seats.map(seat => ( <Seat key={seat.id} seat={seat} onSelect={onSelect} /> ))} </div> </div> ); }
整体布局组件(TheaterLayout.jsx)
整合屏幕、座位区和图例,是页面的核心展示组件:
import SeatRow from "./SeatRow"; export default function TheaterLayout({ seatsData, selectedSeats, onSelect }) { return ( <div className="flex flex-col items-center p-4"> {/* 屏幕区域 */} <div className="w-3/4 h-8 bg-gray-800 mb-8 rounded-md flex items-center justify-center text-white"> 屏幕 </div> {/* 座位区域 */} <div className="space-y-2"> {seatsData.map(row => ( <SeatRow key={row.row} row={row} onSelect={onSelect} /> ))} </div> {/* 状态图例 */} <div className="flex items-center mt-8 space-x-6"> <div className="flex items-center"> <div className="w-6 h-6 bg-yellow-500 rounded-md mr-2"></div> <span>已售出</span> </div> <div className="flex items-center"> <div className="w-6 h-6 bg-white border border-gray-300 rounded-md mr-2"></div> <span>可预订</span> </div> {selectedSeats.length > 0 && ( <div className="flex items-center"> <div className="w-6 h-6 bg-blue-500 rounded-md mr-2"></div> <span>已选中</span> </div> )} </div> </div> ); }
3. 页面层动态获取数据
在NextJS页面中,根据影院ID从API获取对应座位数据,传递给布局组件:
// pages/theaters/[theaterId].jsx import { useEffect, useState } from "react"; import TheaterLayout from "../../components/TheaterLayout"; export default function TheaterSeatPage({ theaterId }) { const [seatsData, setSeatsData] = useState([]); const [selectedSeats, setSelectedSeats] = useState([]); // 获取影院座位数据 useEffect(() => { const fetchSeats = async () => { const res = await fetch(`/api/theaters/${theaterId}/seats`); const data = await res.json(); setSeatsData(data); }; fetchSeats(); }, [theaterId]); // 处理选座逻辑 const handleSeatSelect = (seatId) => { setSelectedSeats(prev => prev.includes(seatId) ? prev.filter(id => id !== seatId) : [...prev, seatId] ); }; if (!seatsData.length) return <div className="text-center py-8">加载中...</div>; return ( <div className="container mx-auto py-8"> <h1 className="text-2xl font-bold mb-6 text-center">选择您的座位</h1> <TheaterLayout seatsData={seatsData} selectedSeats={selectedSeats} onSelect={handleSeatSelect} /> </div> ); } // 服务器端渲染获取影院ID export async function getServerSideProps(context) { const { theaterId } = context.params; return { props: { theaterId } }; }
4. 样式说明
示例中使用Tailwind CSS实现样式,如果你用CSS模块,只需将类名替换为对应的自定义样式即可:
- 已售出座位:黄色背景,禁用状态
- 可预订座位:白色背景,鼠标 hover 时变浅灰
- 选中座位(可选扩展):蓝色背景区分
内容的提问来源于stack exchange,提问作者Shiv Yadav
相关产品推荐
相关产品推荐

