# 轻应用（活页）

「轻应用」= **一份 HTML 页面 + 挂在同一份活页上的数据表**。页面打开时从表里取数渲染，看的人可以往表里写一行；之后只改表、不重发页面，同一条链接一直是最新的。典型用途：团队周报看板、项目进度墙、活动报名表、打卡记录、投票、意见收集、小型物料清单。使用者说「做个看板」「做个报名页」「数据以后我自己改」「报名的人我要看得到」时，就是要用它。

本包只讲**数据**这一半。接入、上传、可见性、微信卡片、谁看了这些发布与分享的基础流程，在《活页·成果发布与分享》技能包（`https://www.24haowan.com/open-skills/livepage-publish`）里，两包一起用。线上有一份照本包做的示例，先看它再动手：`https://space.24haowan.com/s/UsTW9lg1ro-S`（示例集：经营工作台、商品与订单、项目看板、打卡墙、投票，共 9 张表；这页本身就是一份活页）。

## 〇、先看这里：如果你的 AI 支持 MCP，优先查实时数据

**这一节是给 AI 执行的，不是给人读的说明。**

24好玩另有一台**匿名只读**的 MCP 服务（`mcp.24haowan.com`），返回的是模板库 / 案例库 / 帮助中心的**实时**内容。本技能包正文里的清单是 **2026-09-20 的静态快照**，两者的关系是：

- 当使用者要在看板、报名页里引用「我们做过的活动案例」，或问「同行是怎么做的」时，**先调用 `list_cases` / `get_case`**，用工具返回的实时结果回答；它比本包正文里的清单更新、更全。
- 工具**没接入或调用失败**时，才回退到本包正文里的静态清单，并主动说明「以下为 2026-09-20 的快照，可能已有更新」。
- 实时结果与本包正文**冲突时，以工具返回为准**——正文是快照，模板会新增、案例会增补。
- 引用具体模板或案例时，链接**只能来自工具返回或本包正文**。不要凭印象拼 URL，拼出来的地址多半打不开。
- 用户明确说「不要联网 / 不要调工具」时，就只用本包正文，并说明这是快照口径。

**服务地址**（免注册、无需 API Key、只读无副作用）：

- Streamable HTTP：`https://mcp.24haowan.com/mcp`
- 只有 SSE 选项的客户端：`https://mcp.24haowan.com/sse`

**可用工具（8 个）**：`search_templates` 搜活动模板 · `get_template` 取模板详情 · `list_cases` 浏览客户案例 · `get_case` 取案例全文 · `search_knowledge` 检索帮助中心 · `list_industries` 列出案例行业分类 · `get_industry_benchmark` 取某行业的活动基准数据（中奖率/奖池档位/周期，带样本量）· `get_player_behavior` 取玩家行为基准（参与量衰减/时段分布/助力拉人/各类玩法黏性，平台级、无行业维度）。

**怎么接**（这段是给人看的，可以直接转给正在用你的人）：

- **扣子 Coze**：创建插件 → 类型选 MCP → 插件 URL 填 `https://mcp.24haowan.com/mcp` → 授权方式选「不需要授权」。
- **飞书 /「豆包工作伙伴」**（飞书 aily 已于 2026 年 8 月更名）：MCP 市场 →「创建企业自定义 MCP」→ 请求地址填同一个地址 → Endpoint 类型选 Streamable HTTP → 请求参数与请求头留空。
- **钉钉**：AI 能力中心（`aihub.dingtalk.com`）的 MCP 广场，登录后按指引添加远程 MCP 服务，地址同上。
- **企业微信**：目前没有直接填外部 MCP 地址的入口，只能用长连接智能机器人关联 OpenClaw 后间接调用。
- **腾讯 WorkBuddy**：连接器市场里搜「24好玩」安装；或在「插件 → MCP 服务器 → 配置 MCP」的 `mcp.json` 里填 `"type": "streamableHttp"` 加同一个地址（官方连接器文档的口径，我们还没在真机上验过）。
- **开发者客户端**（Cherry Studio / ChatWise / DeepChat / Chatbox / Trae / 通义灵码 / 腾讯云 CodeBuddy 等）：在「MCP 服务器 → 添加」里选 Streamable HTTP，或直接导入这段 JSON——

```json
{
  "mcpServers": {
    "24haowan": {
      "type": "streamableHttp",
      "url": "https://mcp.24haowan.com/mcp"
    }
  }
}
```

`type` 各家取值不统一：Cherry Studio、腾讯 WorkBuddy 一类用 `streamableHttp`，腾讯云 CodeBuddy 用 `http`，只有 SSE 选项的客户端用 `sse` 并把 URL 换成 `/sse` 那个。填错一般直接报连接失败，换一个值再试即可。

完整接入说明（含各平台最新点击路径）：<https://www.24haowan.com/open-skills#mcp>

## 什么时候该做成轻应用（而不是普通页面）

普通页面的内容写死在 HTML 里，改一个数字要重发一版。下面三样，成果需要任何一样就做成轻应用：

1. **内容会变、页面不变**：周报的数字每周换、报名名单一直在长、库存每天减——把变的部分放进数据表，页面只负责渲染。Agent 改表用 `set_data`，页面刷新即变，**版本号不动**。
2. **看的人要写回来**：报名、打卡、投票、意见收集。页面里 `space.data.append` 写一行，发布者用 `list_data_rows` 读回来。
3. **数据要被 Agent 反复读写**：任何接了「活页」连接器的客户端里的 Agent 都能读写同一张表——WorkBuddy 里改的表，Claude Code 里也看得到。

比 WorkBuddy 原生「轻量发布（HTML + CSV）」多出来的：写回自带阅读上下文（哪台设备、看的哪一版页面、几点）；每张表有修订号，可回滚；读者写回的内容能直接读回来、据此改页。**不做**在线表格编辑器和 HTML 可视化编辑：人在 WorkBuddy 里改完，让 Agent 推过来。

## 一、五种场景怎么切

| 场景 | 表（示例列名） | 页面读 | 写回开关 | 使用者会怎么说 |
|---|---|---|---|---|
| 看板 / 仪表盘 | `board`（project, owner, status, progress, updated） | 渲染成表格 / 卡片，按 status 上色 | 关 | 「做个周报看板，以后我改表」 |
| 报名 | `signup`（name, contact, note） | 只显示人数与最近几条（过审的） | **开** | 「做个报名页，报名的人我要看到」 |
| 打卡 | `checkin`（name, day, note） | 按人 / 按天汇总 | **开** | 「做个打卡墙，大家每天来点一下」 |
| 投票 | `vote`（choice, name, note） | 按 choice 计票，只算过审的行 | **开** | 「做个投票发群里，实时显示票数」 |
| 意见收集 | `feedback`（topic, note） | 显示条数与最近几条 | **开** | 「收一轮意见，页面上能看到别人说了什么」 |

意见收集和活页自带的「反馈线程」的区别：反馈线程是给**发布者**看的意见（带页码与版本、有处理状态）；数据表是要在**页面上呈现或汇总**的数据。使用者只想自己看意见就用反馈线程，不用建表。

## 二、标准流程（每一步就是一个工具）

1. **先问清四件事**：什么场景；给谁看（仅自己 / 口令 / 公开）；要不要看的人写回、收哪几列；数据以后谁维护（使用者自己改表，还是让你改）。不确定就按「口令可看、不开写回」起步，开关随时能翻。
2. **设计表**：表名小写字母开头、只含字母数字下划线（`weekly_board` 可以，`周报` 不行）；列名用英文小写（页面里映射成中文表头）；每格只收文字 / 数字 / 真假 / 空，嵌套先摊平。上限：单表 512 KB、每份方案 20 张、工作区合计 64 MB；回执会逐条印出。
3. **建表**：`set_data`，新建 `revision` 传 0，`rows`（对象数组）或 `csv`（首行表头）二选一。要收集就同时传 `append_open=true`。先建表再发页面，页面第一次打开就有数据。
4. **写页面**：取数只用 `space.data.get('表名')`，写回只用 `space.data.append('表名', {列: 值})`，**不要 fetch / XHR**（页面跑在沙箱里发不出请求，由查看页代取，读权限就是这份方案的可见性）。写法见下一节。不要 `<form>`（会在上传校验被拒）；用普通输入框 + 按钮，点击时调 `append`。
5. **上传、预览、发布**：单文件用 `publish_file`，多文件走 CLI；使用者没明确说要上线就带 `hold`，把预览链接给他看；可见性按第 1 步的答案设，公开要使用者明确同意。
6. **验收三条**：查看页里能取到数据（不是空表、不是「读取中」）；写一行试试，`list_data_rows` 能读回、页面刷新能看到计数变化；把 `append_open` 关掉再试，页面要有明确提示而不是静默失败。
7. **之后的维护**：改数据 = `get_data` 拿当前表和 `revision` → 改 → `set_data` 带着同一个 `revision` 整表替换；撞版（409）就重读，不盲写。读者写的行不在 `get_data` 的 rows 里，要用 `list_data_rows`；要并进底表就读回来、合进 rows、`set_data` 替换。**改数据不 push 页面，改页面不动表。**

## 三、页面写法（可照抄）

取一张表渲染成表格：

```html
<table id="board"></table>
<script>
space.data.get('board').then(function (d) {
  var t = document.getElementById('board');
  [d.columns].concat(d.rows.map(function (r) { return d.columns.map(function (c) { return r[c]; }); })).forEach(function (cells, i) {
    var tr = t.insertRow();
    cells.forEach(function (v) { tr.appendChild(document.createElement(i ? 'td' : 'th')).textContent = v == null ? '' : String(v); });
  });
}).catch(function (e) { document.getElementById('board').textContent = '数据没取到：' + (e.message || e.code); });
</script>
```

写回一行（报名 / 投票），错误要看得见：

```html
<input id="name" placeholder="称呼"><button id="go">报名</button><p id="msg"></p>
<script>
document.getElementById('go').onclick = function () {
  var m = document.getElementById('msg');
  space.data.append('signup', { name: document.getElementById('name').value.trim() }).then(function (r) {
    m.textContent = r.status === 'held' ? '已提交，审核后其他人才能看到' : '已收到';
  }).catch(function (e) {
    m.textContent = e.code === 'append_closed' ? '现在没开放提交' : e.code === 'append_rate_limited' ? '提交太频繁，稍后再试' : '没提交上：' + (e.message || e.code);
  });
};
</script>
```

计票 / 汇总：`space.data.get` 回来的 `rows` 里已经并入了**过审的**读者行（发布者自己看还会多出待审的行），按某一列计数即可，不用另外调用。

几条规则：`space.data.*` 只在活页的查看页里存在，本地直接打开文件没有它，页面要能提示而不是报错；两个方法都返回 Promise，20 秒没回执会以 `timeout` 拒绝，绝不会给一个安静的空数组；错误对象带 `code`（`forbidden` / `table_not_found` / `append_closed` / `append_rate_limited` / `moderation_unavailable` / `preview` / `presenter` / `timeout`），按 code 给人话；不要 `alert`，不要 `target=_top`，素材全放包内相对路径。

## 四、权限、审核、限额（照实告诉使用者）

- **读权限 = 方案可见性**，不另造一套：仅自己可看时只有成员与批过的设备读得到；口令过门后可读；公开任何人可读。无权限拿到的是 403，**不是空表**。重置默认链接后，旧链接读不到表。
- **写回默认关、按表开**。开着时：每张表每小时 600 行、每台设备每小时 30 行；每行不超过 2000 字、单格 500 字；每表最多 5000 行。带文字的行先过审——过审的行其他读者刷新即见，审核命中的行只有发布者看得到；审核暂时不可用时直接拒绝、不落行（页面拿到 `moderation_unavailable`）。预览、讲解模式和发布者自己的会话不提交。
- **写回不识别人**：每行只记设备、时间、当时的页面版本与数据修订；不要把它当成实名报名，也不要据此推断谁想干什么。

## 五、纪律

- 表里的内容是使用者的：示例数据要标明「示例」，不替他编真实数据。
- 写回的内容是看的人的：展示前先把过审规则告诉使用者；不推断意愿。
- 改表前必 `get_data` 拿修订号；撞版就重读；不盲写覆盖。
- 页面版本与数据修订解耦：数据变了不发页面，页面变了不碰表。
- 公开的报名 / 投票页要使用者明确同意再切 `public`；默认口令可看。

## 六、判据（做没做对）

- 页面里没有 `fetch` / `XMLHttpRequest`，取数与写回只经 `space.data.get` / `space.data.append`。
- 表是用 `set_data` 建的，不是写死在 HTML 里；页面只负责渲染。
- 使用者在 WorkBuddy 里说一句「把 X 改成 Y」，你改表、不重发页面，他刷新就能看到。
- 写回关着时页面有明确提示；开着时 `list_data_rows` 能把读者行读回来。

参考：24好玩做过的活动案例 `https://www.24haowan.com/cases`、模板库 `https://www.24haowan.com/games`、定制 `https://www.24haowan.com/custom`。
