其他
用 React 19/Vite + Google Maps…
适用于把多家庭公路旅行做成深色 ops 仪表盘,集中管理车队路线、到达窗口、逐日时间线、餐饮后勤、费用分摊与家庭清单。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
用 React 19/Vite + Go特定
解决思路
- 运行 git clone https://github.com/andrewjiang/palantir-for-family-trips.git 克隆项目。
- 进入项目目录:cd palantir-for-family-trips。
- 安装依赖:npm install。
- 复制环境变量模板:cp .env.example .env。
- 编辑 .env,填入 VITE_GOOGLE_MAPS_API_KEY=your_browser_maps_key_here;如需 Cloud 样式或 Advanced Markers,再填 VITE_GOOGLE_MAP_ID=your_optional_google_map_id。
- 启动开发服务器:npm run dev。
- 在浏览器打开 Vite 打印的 URL,通常为 http://127.0.0.1:5173。
- 按项目结构放置文件:src/App.jsx 主壳、标签、时间线、覆盖层;src/CommandMap.jsx Google Maps 路线渲染与车队播放;src/tripModel.js 种子行程文档、家庭、路线、辅助函数;src/main.jsx React 入口;src/index.css 全局深色主题样式;public/ 与 docs/ 保存静态资源和 README 截图。
- 在 src/tripModel.js 中维护单一 trip 对象,把所有数据(families、routes、days、meals、activities、expenses、checklist)都放在这里。
- 按示例形状定义 trip:包含 name、basecamp(label/coords)、families(id/name/origin/members/vehicle/arrivalWindow/color)、days(date/label/events)、meals、activities、expenses、checklist。
- 在 tripModel.js 中实现 getFamily(id) 辅助函数:trip.families.find((f) => f.id === id)。
- 在 tripModel.js 中实现 getDayEvents(dateStr) 辅助函数:按 trip.days.find((d) => d.date === dateStr) 返回 events,未命中返回 []。
- 在 tripModel.js 中实现 splitExpense(expense) 辅助函数:用 expense.amount / expense.splitAmong.length 计算每家庭份额,并返回 { famId, owes } 列表。
- 添加新家庭时编辑 src/tripModel.js 的 families 数组,新增对象并包含 id、name、origin.label、origin.coords、members、vehicle、arrivalWindow.earliest/latest、唯一的 color。
- 确保新家庭 color 唯一,以便地图 polyline 与 UI 强调色可区分。
- 依赖 trip.families 的 map、timeline、family view 会自动读取新增家庭;不要在组件里写死单个家庭数据。
- 在 src/App.jsx 中引入 CommandMap 和 trip,并把 families={trip.families}、basecamp={trip.basecamp}、playbackTime={currentPlaybackTime}、activeFamily={selectedFamilyId}、onMarkerClick={(familyId) => setSelectedFamilyId(familyId)} 传给 CommandMap。
- 在 src/CommandMap.jsx 中用 Directions Service 为每个家庭构建路线:调用 directionsService.route(),传入 origin: family.origin.coords、destination: trip.basecamp.coords、travelMode: google.maps.TravelMode.DRIVING。
- 如需给路线添加途经点,例如加油站,在 directionsService.route() 的 waypoints 数组中加对象:{ location: { lat: 37.65, lng: -120.95 }, stopover: false }。
- 在 directionsService.route() 回调中判断 status 等于 OK,然后用 directionsRenderer.setDirections(result) 渲染路线。
- 在 src/App.jsx 中实现时间线状态:用 const [dayIndex, setDayIndex] = useState(0),用 const currentDay = trip.days[dayIndex] 取当前天。
- 实现 advanceDay():setDayIndex((i) => Math.min(i + 1, trip.days.length - 1)),用当前 dayIndex 与播放偏移驱动地图和时间线面板。
- 渲染当前天事件:currentDay.events.map((event) => <TimelineEvent key={event.time} event={event} families={trip.families} />)。
- 新增标签页时,在 src/App.jsx 的 TABS 数组中添加 { id: 'packing', label: 'Packing' } 这类条目。
- 在标签内容 switch 中添加条件渲染:{activeTab === 'packing' && (<PackingView checklist={trip.checklist} families={trip.families} />)}。
- 为新标签创建组件,如 src/PackingView.jsx,接收 checklist 和 families,按 families.map(...) 渲染每个家庭的 checklist[fam.id] 列表,并用 fam.color 设置标题颜色。
- 复用 Framer Motion 全屏覆盖层:从 framer-motion 引入 AnimatePresence 和 motion,用 showOverlay 条件包裹 motion.div,设置 initial={{ opacity: 0, scale: 0.95 }}、animate={{ opacity: 1, scale: 1 }}、exit={{ opacity: 0, scale: 0.95 }}、transition={{ duration: 0.3, ease: 'easeOut' }}。
- 在全屏覆盖层中加入内容,如 <h1>MISSION LAUNCH</h1>,以及关闭按钮,按钮 onClick 调用 setShowOverlay(false)。
- 计算费用分摊时引入 trip 和 splitExpense:trip.expenses.forEach((exp) => { const splits = splitExpense(exp); ... }),再按 famId 从 trip.families 找家庭并输出 owes。
- 在代码中通过 import.meta.env.VITE_GOOGLE_MAPS_API_KEY 读取 Maps key,通过 import.meta.env.VITE_GOOGLE_MAP_ID 读取 Map ID。
- 所有行程数据优先改 src/tripModel.js;UI 应设计为响应 trip 数据变化,而不是在多个组件里重复维护。
- 为地图 polyline、时间线强调色和 checklist 标题统一使用 family.color,并选择在深色背景上有足够对比度的 hex 值。
- 默认使用本地状态,无后端、无认证;如需刷新后保留修改,给 trip 数据加 localStorage 持久化。
- 保持桌面端密集仪表盘布局;如未额外开发,不要期望开箱即用的移动端响应式布局。
- 部署使用 Google Maps key 时,将用量计入对应 Google Cloud 项目并限制 key 到自己的域名。
- 地图不加载时,检查 .env 的 VITE_GOOGLE_MAPS_API_KEY,并在 Google Cloud Console 为 key 启用 Maps JS API 和 Directions API。
- 出现 google is not defined 时,确认 Maps 脚本异步加载,并且 CommandMap 只在 onLoad 回调内调用 Maps API。
- 路线不渲染时,确认 Google Cloud 项目中已单独启用 Directions API。
- Vite 开发服务器 URL 不同时,使用 Vite 实际打印的 URL,不要写死端口。
- 新增家庭导致布局异常时,检查新家庭 id 和 color 是否唯一,并把 App.jsx 中硬编码的 familyId 引用改为 trip.families.map(...) 等通用逻辑。
适用边界 · 注意事项
- 没有 VITE_GOOGLE_MAPS_API_KEY 或不想启用 Google Cloud Maps/Directions API 与计费时,不适合使用需要实时地图和路线播放的功能;其他视图仍可用但地图层不会初始化。
- 需要移动端优先或响应式布局时,该仪表盘默认针对桌面端优化,不适合直接拿来当移动端应用。
- 只需要简单静态行程表、不需要车队地图、时间线回放、餐饮后勤、费用分摊等多家庭协作时,不值得按此复杂度搭建。