【免费下载链接】palantir-for-family-trips
A Palantir-ish dashboard for family trip planning.
palantir-for-family-trips 是一个把家庭旅行做成"作战指挥中心"的开源规划看板。它的核心不是数据库,而是一份完整的trip document(旅行文档):家庭、地点、路线、餐饮等 9 类实体全部装在这份 JSON 文档里,靠linkedEntityKeys互相引用,整体持久化在浏览器 localStorage 中。读懂这份文档的结构,就读懂了整个应用。
为什么用"一份旅行文档"管理整个行程?🧭
多家庭同游本质上是个小型系统问题:三个家庭从不同城市错峰出发、共享一处营地、轮流订餐、还要分摊费用。传统做法是多张表格加群聊记录,而 palantir-for-family-trips 选择把所有信息收敛成单一事实来源:
- 没有后端、没有数据库,整份 trip document 存进 localStorage,存储键为
trip-command-center/v4-public(见 tripModel.js) - Itinerary、Meals、Activities 等页面只是同一份文档的不同"投影"
- 修改任何实体(比如勾掉一个任务),整份文档重新渲染,数据天然一致
持久化逻辑薄到不能再薄:usePersistedTripState.js 就是一个"读 JSON → 存 JSON"的 React Hook,真正的复杂度全部集中在 tripModel.js 的文档模型里。
trip document 的 9 类实体全景 📐
初始化函数 createInitialTripDocument 返回的文档结构一目了然——9 个实体集合加少量 UI 状态:
| 实体类型 | 文档字段 | 对应页面 | 负责什么 |
|---|---|---|---|
| family | families | Families | 家庭单位:出发城市、ETA、车辆、准备度 |
| location | locations | Stay / Meals 等 | 地点:营地、餐厅、补给点、公园入口 |
| route | routes | Itinerary | 路线:起终点、途经点、回放窗口 |
| itineraryItem | itineraryItems | Itinerary | 时间轴上的行程段(startSlot+span) |
| meal | meals | Meals | 每一餐:时间、地点、状态、负责人 |
| activity | activities | Activities | 每日主题:风险、天气敏感度、降级方案 |
| stayItem | stayItems | Stay | 住宿事务:门禁、分房、停车、退房 |
| expense | expenses | Expenses | 费用:付款人、金额、分摊模式 |
| task | tasks | Itinerary / Families | 待办任务:归属家庭、完成状态 |
类型到集合字段的映射写死在 COLLECTION_BY_TYPE,实体到页面的映射在 ENTITY_PAGE。注意location是特例——它被多个页面共享,isEntityOnPage 里专门放宽了归属规则,因为餐厅、营地、湖滩天然跨场景使用。
9 类实体逐个拆解 🔍
人:family 与 task
family实体记录出发地、ETA、车辆和readiness准备度,并用taskIds直接挂着自家待办。每个task有ownerFamilyId和status,getFamilyReadiness 直接用"已完成任务 ÷ 总任务"实时算出准备度——派生指标不落库,永远新鲜。
地:location
location用category细分为stay(营地)、meal(餐厅)、activity(活动区)、logistics(补给点)、park(公园入口)。营地实体还带入住/退房时间、Wi-Fi、照片等完整字段,是所有路线的目的地锚点。
动:route 与 itineraryItem
这是最有"指挥中心味道"的一对:itineraryItem是时间轴上的色块,route是地图上的线与点。
- route 有
originCoordinates、destinationLocationId、stopLocationIds,路径由 synchronizeRoutePaths 从 locations 的坐标自动拼装——坐标只存一份,永不漂移 - route 用
simulationStartSlot / simulationEndSlot定义回放窗口,simulationMilestones甚至能模拟中途停车 - 路线与行程段通过
linkedEntityKey双向绑定
事:meal、activity 与 stayItem
meal记录每餐的timeLabel、locationId和reservationType(Cook-in / Pack-out / Walk-in);activity是每日"主题任务",带status(Go/Watch)、riskLevel、weatherSensitivity和backup降级方案;stayItem则把住宿拆成门禁、分房、停车、退房四个可独立追踪的事务。
钱:expense
expense除了金额和付款人,还有allocationMode(equal / individual / manual)和allocations明细字典——分摊规则与分摊数据分离,改规则不动数据。
linkedEntityKeys:用字符串键把实体串成一张图 🕸️
整套模型最精妙的设计在 makeEntityKey:它把"类型 + id"拼成family:north-star、meal:thu-dinner这样的字符串键。每个实体都可以携带linkedEntityKeys数组指向其他实体,不建外键表,一张轻量引用网就搭好了:
- 选中一个 activity,getLinkedEntities 顺键找出它关联的行程段、餐饮和地点
- getTimelineContext 用时间轴光标找出"正在发生"和"即将发生"的实体,再顺藤摸出关联的未完成待办
- 界面里的"关联实体"、"规划任务"面板,都是同一张网的投影
slot 时间轴:让所有页面对齐同一把标尺 ⏱️
tripData.js 定义了 4 天(DAYS)和每天的 4 个时段(TIME_SLOTS = ['00', '06', '12', '18'],每格 6 小时)。全行程因此被拉成一条 0~16 的 slot 数轴:startSlot: 2.09表示第 1 天第 3 个时段后 0.09 格,约等于周四 12:32。getSlotLabel 能把 slot 反解成Thu 12:32 PM这样的人类可读时间。行程段、餐饮、路线回放全部共用这把标尺,这就是"一条时间线统领所有页面"的底气。
版本化持久化:老数据如何平滑升级 📦
存储键里的v4是版本号,历史键(v1~v3)列在 LEGACY_TRIP_DOCUMENT_STORAGE_KEYS。升级路径分三步:
- migrateLegacyState:把老结构(比如内嵌在家庭里的 checklist)映射成独立的 task 实体
- refreshSeededDoc:用种子数据刷新文档,同时保留用户改过的字段(准备度、任务状态、费用明细等)
- projectTripDocument:公开分享模式下把家庭住址、Wi-Fi 密码等隐私字段全部洗掉
一套"迁移 + 刷新 + 脱敏"流程,让浏览器里的单文档能像正经数据库一样平滑演进。
给初学者的 3 点设计启发 💡
- 单一事实来源比"小而全"更简单:一个 JSON 文档 + localStorage,没有 schema 迁移烦恼,却同时支撑了六个页面
- 用字符串键建引用图:
type:id键比外键表轻得多,非常适配前端单文档模型 - 派生值不落库:准备度、时间标签、路线 path 全部实时计算,文档只存最小事实
palantir-for-family-trips 把一次普通家庭周末硬生生做成了"作战指挥",而全部秘密就在这份结构清晰、引用自洽、可迁移可脱敏的 trip document 里。想动手改造,从 tripModel.js 入手,配合 README.md 了解启动方式、DESIGN.md 了解界面设计即可上手。
【免费下载链接】palantir-for-family-trips
A Palantir-ish dashboard for family trip planning.
相关推荐
Univer数据模型设计:文档、表格与幻灯片的统一数据管理
Univer数据模型设计:文档、表格与幻灯片的统一数据管理 Univer作为一套企业级文档和数据协作解决方案,整合了电子表格(Spreadsheets)、文档(
前端企业应用突破文档碎片化困境:DoclingDocument统一数据模型设计解析
突破文档碎片化困境:DoclingDocument统一数据模型设计解析 你是否还在为处理多种格式文档时遇到的结构不统一、信息提取困难而烦恼?无论是PDF中的复杂
AI 应用计算机视觉OCRkcmd 语义模型实战 Codelab:一份语义本体,贯通 BigQuery 与 Spanner 的同一数据旅程
kcmd 语义模型实战 Codelab:一份语义本体,贯通 BigQuery 与 Spanner 的同一数据旅程 本指南以仓库内 toolbox/mdcode/
数据目录AI Agent人工智能知识管理示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考