行程 PWA 生成器
把一次出行变成一个能装到手机主屏的网页 App:离线可用、天气实时刷新、费用记账、导航一键直达。一次生成,全家用、随手看。
适合谁 / 不适合谁
- ✅ 适合:家庭游 / 亲子游 / 个人出行 / 团队出差,想要一个"不依赖 App、不联网也能看、手机桌面一点就开"的行程页。
- ✅ 适合:需要把天气、导航、花销、待办集中在一个页面的人。
- ❌ 不适合:需要多人实时协作编辑、评论、共享相册的复杂场景(那是协作文档的事)。
- ❌ 不适合:纯文字攻略、不需要手机端随时查看的轻量需求(直接用笔记就够)。
30 秒 Quickstart
- 告诉我:去哪、几号到几号、几个人、大交通(航班/车次)、住哪、每天想去哪。
- 我在工作区生成
itinerary-<目的地>/(含完整 PWA 文件)。 - 本地预览:直接双击
index.html即可在浏览器看效果(推荐用手机扫码或同局域网访问看真机效果)。 - 长期手机访问(可选):部署到任意静态托管 / CloudStudio,拿到一个 https 链接,存书签、加到主屏。
> 想先看成品?看 examples/demo-shanghai/(一个真实上海 3 日游示例,打开即见)。
触发条件
当用户要求为一次出行/旅行/亲子游创建可添加到手机主屏的行程单、行程卡或 PWA 小程序时,使用本 skill。
目标
生成一个纯前端 PWA(HTML + Manifest + Service Worker + 图标),包含:
- 目的地、日期、人员、预算页头
- 大交通(航班/高铁)信息
- 每日行程卡片(天气/穿衣/交通/餐饮/住宿/导航)
- 出发前待办清单(本地存储)
- 费用记账模块(localStorage,已付/预估)
- 实时天气刷新按钮(Open-Meteo 免费接口,无需 key)
- 导航按钮(国内默认高德;海外场景可选 Google Maps,见下方模板)
- 可部署到任意静态托管 / CloudStudio,生成手机可访问链接
必须收集的信息
- 目的地、出发日期、返程日期、天数、人员组成。
- 大交通:去程/返程航班号或车次、起降时间/车站、票价、是否已订。
- 酒店:名称、地址、坐标(经纬度)、价格、是否已付。
- 每日安排:景点、餐饮、交通方式、风险等级、注意事项。
- 必抢票务/预约提醒(如博物馆提前 N 天放票)。
- 预算与已付金额。
> 信息不齐也能先出框架,占位处标注「待补」,后续让智能体回填即可。
输出目录结构
在工作区创建 itinerary-<目的地>/ 目录,包含:
- index.html — 主页面
- manifest.json — PWA 配置
- sw.js — 离线缓存
- icon.svg — Android/通用图标
- icon-512.png — iOS 主屏图标
- gen_icon.py — 生成 PNG 图标的脚本
核心实现要点
1. index.html 结构
- 主题色默认
#4A3F8F→#6E59C7蓝紫渐变(可在 manifest/theme_color 中调整)。 <head>中必须包含:
html <link rel="manifest" href="manifest.json"> <link rel="apple-touch-icon" href="icon-512.png"> <link rel="icon" type="image/svg+xml" href="icon.svg"> <meta name="theme-color" content="#4A3F8F">- 页头、航班、置顶待办、每日卡片、费用模块、天气刷新按钮、页脚。
- 费用模块 JS 模板(
localStoragekey 带版本,避免冲突):
javascript const KEY="<destination>_trip_exp_v1"; const DEFAULTS=[ {n:"项目1(已付)",a:0,p:true}, {n:"项目2(预估)",a:0,p:false} ]; - 天气刷新函数:
javascript const url="https://api.open-meteo.com/v1/forecast?latitude=<LAT>&longitude=<LNG>"+ "&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max,wind_speed_10m_max"+ "&timezone=Asia%2FShanghai&start_date=<YYYY-MM-DD>&end_date=<YYYY-MM-DD>";
关键坑:forecast_days与start_date/end_date互斥,绝对不能同时传。 - 导航按钮(国内默认高德,海外用 Google Maps):
html <!-- 高德(中国场景默认) --> <a href="https://uri.amap.com/marker?position=<LNG>,<LAT>&name=<NAME>&src=sh&coordinate=gaode&callnative=1">高德导航</a> <!-- Google Maps(海外场景备选,query 顺序是 LAT,LNG 纬度在前) --> <a href="https://www.google.com/maps/search/?api=1&query=<LAT>,<LNG>">Google 导航</a>
> 二选一展示即可;若目的地在中国大陆用高德,海外用 Google Maps,避免给用户一个用不了的链接。
2. manifest.json
{
"name": "<目的地>亲子游 · <日期>",
"short_name": "<目的地>",
"start_url": ".",
"scope": ".",
"display": "standalone",
"background_color": "#F3F1FA",
"theme_color": "#4A3F8F",
"icons": [
{"src": "icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable"},
{"src": "icon-512.png", "sizes": "512x512", "type": "image/png"}
]
}
3. sw.js
const CACHE = '<destination>-trip-v1';
const FILES = ['./', './index.html', './manifest.json', './icon.svg', './icon-512.png'];
self.addEventListener('install', event => {
event.waitUntil(caches.open(CACHE).then(c => c.addAll(FILES)).then(() => self.skipWaiting()));
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))))
.then(() => self.clients.claim())
);
});
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url);
// 实时天气接口直接放行,不缓存
if (url.hostname === 'api.open-meteo.com') {
event.respondWith(fetch(event.request));
return;
}
// 同域资源:网络优先
if (url.origin === self.location.origin) {
event.respondWith(
fetch(event.request).then(resp => {
if (resp && resp.status === 200) {
const copy = resp.clone();
caches.open(CACHE).then(c => c.put(event.request, copy));
}
return resp;
}).catch(() => caches.match(event.request))
);
return;
}
// 其它外域:缓存优先
event.respondWith(
caches.match(event.request).then(cached =>
cached || fetch(event.request).then(resp => resp)
)
);
});
4. 图标
- 复用本 skill assets/ 下的
icon.svg和gen_icon.py。 - 如需体现目的地特色,编辑 SVG 中的白色图形(如东方明珠、山水等)。
- 生成 PNG:
python gen_icon.py→ 输出icon-512.png。 - PNG 背景应不透明,避免 iOS 主屏补黑底。
5. 部署(三种方式任选,不强制)
- 方式 A · 本地预览(最快):直接双击
index.html用浏览器打开即可查看内容与交互。注意file://下 Service Worker / PWA 安装不生效,但页面本身可看——适合先确认效果。 - 方式 B · 任意静态托管(推荐长期用):把整个
itinerary-<目的地>/目录传到任意支持静态文件的服务器(Nginx、GitHub Pages、Vercel、Netlify、对象存储、内网服务器均可)。访问其 https 链接即可,PWA 安装与离线缓存需 https 或 localhost。 - 方式 C · WorkBuddy CloudStudio 一键部署(零运维首选):
bash workbuddy_cloudstudio_deploy --directory <path>/itinerary-<目的地> --entry index.html --port 3000
返回 share link 即手机访问地址。每次更新后重新运行该命令。管理入口:设置 - 数据管理 - 我发布的应用。
更新与缓存
- 每次发布修改后,必须递增
sw.js中的CACHE名称(如 v1→v2),强制浏览器安装新版。 - 若用户反馈"页面没更新",提示重新打开一次链接(或删除主屏图标重新添加,iOS 会缓存图标)。
常见坑
- Open-Meteo 的
forecast_days与start_date/end_date不能同时用,否则返回 400。 - iOS 添加到主屏必须提供 PNG 的
apple-touch-icon,SVG 主屏图标不被支持。 - Service Worker 不能把跨域天气请求缓存优先,否则刷新会拿到旧数据;要对
api.open-meteo.com直接放行。 - 高德
position=是LNG,LAT(经度,纬度),Google Mapsquery=是LAT,LNG(纬度,经度),两者顺序相反,别填反。 - 高德在中国大陆可用,海外(如日本/欧洲)应使用 Google Maps,否则导航链接打不开。
执行步骤
- 确认用户出行信息完整,按"必须收集的信息"逐项补齐(不齐可先出框架占位)。
- 在工作区创建
itinerary-<目的地>/目录。 - 复制本 skill assets/ 下的
icon.svg、gen_icon.py到目录。 - 编写
index.html、manifest.json、sw.js(注意替换所有占位符,导航按目的地选高德或 Google Maps)。 - 运行
gen_icon.py生成icon-512.png。 - 可选部署:按用户需求选方式 A/B/C(见上方部署章节),返回链接即可。
示例
examples/demo-shanghai/:一个真实上海 3 日游的完整示例 PWA,打开index.html即可预览效果,可直接截图作为展示素材,也可照抄其结构。
搜索关键词
行程 / 旅游 / 出行 / 游记 / 攻略 / 行程单 / 行程卡 / PWA / 手机主屏 / 离线网页 / 天气刷新 / 导航 / 费用记账 / 亲子游 / 家庭游 / itinerary / travel / PWA