# 大理 · 洱海 5 日慢游 · 山野假期

> 活页公开模板 `sample-travel-line` · 场景: 行程 · 攻略 · 行业: 旅游出行
> 任何人、任何 Agent 都可以读、照着改，不需要账号。示例内容均为虚构。
> 真页面: https://space.24haowan.com/s/YneyK9-RRT2q · 详情: https://space.24haowan.com/examples/sample-travel-line

## 这是什么

旅行社线路介绍 · 发给客户

## 什么时候用

旅行社、民宿、户外俱乐部把一条成团线路发给客户或社群：首屏一张会动的线路海报，接着目的地真实天气、每天的行程卡、费用包含不含、团期余位，最后收预订咨询，选它。给自己家人排的多城行程选 sample-weekend-trip；自驾路书选 sample-road-trip；按场次收报名、不涉及价格选 sample-event-signup。

## 用户对助手说的原话

帮我们旅行社把一条成团线路做成一个发给客户的网页链接：首屏是一张会动的线路海报，用 SVG 分层画出目的地的山、云、地标和湖，手机上随滚动、电脑上随鼠标轻轻视差，海报上写线路名、一句卖点、团期和「每人多少钱起」，底部两个按钮「选团期」「看行程」，再放一个当地现在气温的小标签；往下第一节「出发前看天气」，用目的地真实的 7 天天气预报（先内联一份今天抓的快照，脚本在时再取实时、3 秒超时，写明数据来源和更新时间），按早晚温差和降雨写一句怎么穿，选中的团期在天气里标出在路上的那几天；然后一张线路小地图和每天一张可以左右滑的行程卡（路线、交通、住宿、餐、一句亮点，配一张 SVG 小插画），滑到哪天地图上亮哪一站；接着费用包含 / 不含两列和退改规则；再是一个月的团期日历，出发日标余位，点一下看还剩几位（用小圆点画）和价格，余位放在一张我能随时改的数据表里；最后一张预订咨询表单（称呼、手机号、团期、成人、儿童，实时算预计团费），存进一张数据表，提交后出一张「顾问会在 30 分钟内联系你」的咨询单。手机竖屏为主，电脑上左边钉住海报、右边是行程；减少动态时动效停住，脚本不跑时也能看全并提交。余位和天气有变就更新同一个链接。

如果你还没有活页连接器（MCP），先读 https://space.24haowan.com/for-agents.md ，照里面的说明接入，或不登录直接发布。

## 照着做一份

1. 读入口文件 https://space.24haowan.com/examples/sample-travel-line/source/index.html（它引用的其他文件见下方列表）。
2. 保留结构、版式和交互，把内容**全部**换成用户自己的；模板里的公司、人名、数字都是虚构的，一样都别留。
3. 交付一个自包含的单文件 HTML：样式写在页面里，不引外部字体 / 脚本 / CDN；手机 375 宽和电脑 1440 宽都要好看，模板有暗色就保留暗色。
4. 要发成微信里能打开、改了链接不变的网页：接入活页（https://space.24haowan.com/for-agents.md）后发布；没有账号也可以让用户在 https://space.24haowan.com/quick 上传这个文件。
5. 没有活页工具、但能发 HTTPS 请求：把做好的 HTML 以 Content-Type: text/html POST 到 https://space.24haowan.com/api/quick?client=template-sample-travel-line ，返回预览链接、7 天分享与认领链接（登录后保留、链接不变）。

## 页面结构

- 线路海报
- 出发前看天气
- 五天怎么走
- 费用包含什么
- 选一个团期
- 预订咨询

## 文件（公开，纯文本）

- https://space.24haowan.com/examples/sample-travel-line/source/index.html（入口）

## 发成活页之后还能做的

- 首屏线路海报：内联 SVG 苍山、玉带云、三塔、洱海分层视差（手机随滚动、电脑随鼠标），往下读太阳慢慢落下
- 目的地 7 天天气两层加载：内联 Open-Meteo 快照先画，脚本在时取实时预报替换；选中的团期在天气里标出「在路上的那几天」，并按温差、降雨给穿衣提醒
- D1–D5 横滑行程卡（scroll-snap），和线路图上的站点联动；选了团期，每张卡自动标上日期
- 团期日历读数据表 departures（发布者用 set_data 改余位，不用重发页面），点日期看余位小圆点和价格
- 预订咨询写回数据表 bookings（原生表单 POST ./_data/bookings），提交后就地出一张可截图的咨询单；页面从不读回 bookings（里面有手机号）
- 同一链接持续更新（余位、天气快照、团期）；微信卡片（share.title / share.desc）

## 发之前要备好

- 数据表 bookings（列 name / phone / date / adults / kids，append_open=true）：预订咨询写进这里，顾问用 list_data_rows 读回回电；里面有完整手机号，页面不读回它，别在页面上加读回
- 数据表 departures（列 date / seats / price，append_open=false）：团期余位与价格，发布者用 set_data 整表替换，页面读它画日历；不建时用页里内联的回落值
- 页面会连 api.open-meteo.com 取天气（免费、无需 key），页里同时内联一份快照，连不上也能显示
- 联系方式类数据表建议设 members_only=true 或口令可看：页面读回会把已过审的行返回给每位读者（官方示例成品就是 members_only，访客的提交不会入表）

## 模板说明（README）

模板市场第三轮补齐空缺的样例（总览见 `core/space/examples/README.md`，品质标准见 `core/space/examples/QUALITY.md`）。虚构旅行社「山野假期」把「大理 · 洱海 5 日慢游」发给客户。旅行社、客栈名、顾问、价格、余位均为虚构；大理、喜洲、双廊等地名是真实地名，天气是真实数据。

## 演示哪个特色

**一张会动的线路海报，加上「选团期 → 留咨询」的回写。**

- **记忆点**是首屏的线路海报：苍山两层山脊、横在半山的玉带云、崇圣寺三塔、洱海、帆船、红嘴鸥全部是内联 SVG，分层摆放。进场时一层层升起来；手机上随滚动做视差，电脑上随鼠标视差；往下读，太阳慢慢落进山后、天色变暖（`.sun` / `.dusk`）。云带、船、鸥、波光是全页唯一的循环动效。暗色下同一张图换成夜景（天色、星星都走 CSS 变量）。
- **出发前看天气，两层加载**（真实数据，大理古城坐标 25.61, 100.27）：页里内联 `WEATHER_SNAPSHOT`（今天实抓的 Open-Meteo 7 天预报 + 抓取时间），HTML 里也预先画好这一份，首屏先显示；脚本在时 `fetch` 实时预报（`AbortSignal.timeout(3000)`），成功就重画、标「实时 · 更新于 HH:MM」，失败就留快照、写「数据更新于 <快照时间>」。按 7 天最低 / 最高温和降雨概率写一句「怎么穿」；选中的团期落在 7 天里的那几天会在天气条上用蓝线标出。始终署名「天气数据：Open-Meteo.com（CC BY 4.0）」。
- **五天行程**：一张线路小地图（洱海、五个站点、路线进入视野时依次画出来）+ D1–D5 横滑卡片（`scroll-snap`），滑到哪天地图上亮哪一站，点地图上的站也能跳到那张卡；选了团期，每张卡自动标上日期。
- **团期日历**读数据表 `departures`（发布者维护，读者不能写）：出发日显示余位，满了禁选，出发前 2 天自动截止；点一下看 12 个小圆点表示的已报 / 剩余、成人价和儿童价，按钮带到表单并同步团期。改余位只要 `set_data`，不用重发页面。
- 发出去之后：文末「预订咨询」= 原生表单 `POST ./_data/bookings`（列 `name / phone / date / adults / kids`），实时算预计团费；脚本在时页内提交，就地出一张可截图的咨询单（「顾问会在 30 分钟内联系你」）。
- **隐私**：`bookings` 里有完整手机号，而页面读回会把已审核的行返回给每个读者，所以**页面从不读 `bookings`**。别给它加「已有 N 人咨询」一类读回；要显示热度就改 `departures` 的余位。
- `prefers-reduced-motion`：海报不进场、不视差、云和船不动，路线直接是画好的。
- 脚本不跑时：海报静止，天气是 HTML 里预画的快照，日历照常显示余位（默认显示 11 月 1 日那一团），表单照常能提交。
- 电脑上：左边钉住海报（`position:sticky`），右边是天气、行程、费用、日历和表单；行程卡一次露两张，带上一天 / 下一天按钮。

## 改哪里变成我的

| 位置 | 改什么 |
|---|---|
| 海报 `.ptop` / `h1` / `.lede` / `.facts` | 旅行社名与标志、线路编号、线路名、一句卖点、团期规律、团的大小、起价 |
| 海报插画 `svg.scene` | 每一层是一个 `<g class="px" data-k="…">`（`data-k` = 视差强度，越近越大）；换目的地就换山脊、地标（`.pagodas`）、水面；颜色全部走 `--sky1` / `--far` / `--lake1` 等变量，亮暗两套在 `:root` 里 |
| 天气 `WEATHER_SNAPSHOT` + fetch 地址里的坐标 + HTML 里预画的 `.wxdays` | 换成目的地坐标；快照格式见下 |
| `packTip()` | 「怎么穿」的几句话（按最低温分档、降雨、起雾） |
| 线路图 `svg.map` | 湖的轮廓 `.lk`、站点 `.st`（`data-d` = 第几天）、路线 `.rt`（一段一条 `<path>`，`.drive` 是送机虚线），`aria-label` 同步 |
| 行程卡 `.day` | 每天一张：小插画 `svg.ill`、标题、路线 / 交通 / 住宿 / 餐、亮点 `.hl`；增减天数时线路图站点、`.railnav` 里的「共 5 天」、脚本里 `addDays(iso, 4)`（返回日）一起改 |
| 费用 `.fees` / `.rules` | 包含、不含、退改规则；`#fees .intro` 写价格档 |
| 团期 `DEPARTURES` + 日历 `.cal` + 表单 `<select name="date">` | 回落值在脚本里；HTML 里的日历格和下拉选项是给不跑脚本时看的，改月份时一起改（`.wk` 从周日起排） |
| 脚本 `KID` / `CAP` / `CUTOFF_DAYS` | 儿童价、每团人数、提前几天截止 |
| 咨询单 `.done` | 单子上的文字、顾问回电时间 |
| 页脚 | 平台标志与名称（与 `core/space/src/theme.ts` 的 `MARK_SVG` / `PUBLIC_NAME` 逐字一致，测试咬着）+ 一句适用场景；发自己的线路时整段删掉 |
| `manifest.json` | `title` / `share.title` / `share.desc`（微信卡片上的字）；节增减时 `sections` 与页里的 `id` / `data-track-section` 三处同改 |

### 天气快照

`index.html` 末尾脚本里的 `WEATHER_SNAPSHOT` 是 Open-Meteo 返回的原样子集，抓取时间写在 `fetched`（北京时间）：

```js
var WEATHER_SNAPSHOT = {
  "fetched": "2026-10-29T06:30",
  "current": { "time": "2026-10-29T06:30", "temperature_2m": 15, "weather_code": 1 },
  "daily": { "time": [...7 天], "temperature_2m_max": [...], "temperature_2m_min": [...], "weather_code": [...], "precipitation_probability_max": [...] }
};
```

刷新办法：`curl` 下面这个地址，把 `current` 与 `daily` 原样贴进去、`fetched` 改成 `current.time`，HTML 里预画的 `.wxdays` 七格、`.now-t` / `.now-w`、海报上的 `.wxchip` 和「数据更新于」那一行按同样的数改掉（给不跑脚本的读者看），再发一版（同一链接）。每日定时任务也只改这几处。想加第二个城市（比如丽江 26.87, 100.23），照这段再抓一份、加一组切换按钮即可。

```
https://api.open-meteo.com/v1/forecast?latitude=25.61&longitude=100.27&current=temperature_2m,weather_code&daily=temperature_2m_max,temperature_2m_min,weather_code,precipitation_probability_max&timezone=Asia%2FShanghai&forecast_days=7
```

## 数据表

| 表 | 列 | 读者写 | 页面怎么用 |
|---|---|---|---|
| `bookings` | `name`, `phone`, `date`, `adults`, `kids` | **开** | 只写不读；顾问用 `list_data_rows table=bookings` 读回回电 |
| `departures` | `date`, `seats`, `price` | **关** | 读它画日历：`seats` 是还剩几位（≤ 0 显示「已满」），`price` 是成人价 |

发之前先建表：

```
set_data  proposal_id: <id>  table: "bookings"  columns: ["name","phone","date","adults","kids"]  rows: []  append_open: true  revision: 0
set_data  proposal_id: <id>  table: "departures"  append_open: false  revision: 0  rows: [
  { "date": "2026-11-01", "seats": 2, "price": 3280 }, { "date": "2026-11-04", "seats": 5, "price": 3280 }, …
]
```

每确认一单，`get_data table=departures` 拿修订号，再 `set_data` 整表替换、把那一团的 `seats` 减掉，读者刷新就看到新余位。本地 `space-cli serve` 会把咨询写进包里的 `_data/bookings.json`，试完记得改回 `[]`。

## 怎么发出去

```bash
export SPACE_TOKEN=sk-space-…            # MCP create_cli_token
node core/space/cli/space-cli.mjs push core/space/examples/sample-travel-line \
  --proposal <proposal_id> --manifest core/space/examples/sample-travel-line/manifest.json --hold
```

没有终端时也可以 MCP `publish_file`（`filename: "index.html"`、`content` 为全文、`manifest` 为 manifest.json 去掉 entry、`hold: true`），再按上面建两张表。
给谁看：默认仅自己可见；发给客户、朋友圈和社群前用 `set_visibility` 改成 `public`。出发前一周刷新一次天气快照再发一版，链接不变。

## 素材

- 没有位图：旅行社标志、海报插画、线路图、行程小插画、天气图标、咨询单全部是内联 SVG；旅行社、客栈、顾问、价格、余位均为虚构。
- 天气：Open-Meteo.com（CC BY 4.0），页面上署名。
- `thumb.webp` / `phone.webp`：画廊缩略图与手机首屏图，由 `examples/verify-shots.mjs --write-thumb --write-phone` 从成品截出，不进发布包。
