# 交互与导航

| 方法 | 参数与行为 |
| --- | --- |
| `button(text, onClick?, variant?)` | 返回 button；`variant` 为 `"primary"`（默认）或 `"secondary"`。 |
| `list(items)` | 返回列表容器，接收节点或节点数组。 |
| `row(label, extra?)` | 返回可点击的 button 行。`label` 必须是字符串（内部直接赋给 `textContent`，传节点会显示成 `[object HTMLElement]`）；`extra` 可以是字符串或节点。 |
| `tabs(items, options?)` | 分段选择；`items` 为 `{ id, label }[]`，`options` 支持 `value`、`onChange(id)`。 |
| `tag(text)` | 返回文字标签，不包含点击行为。 |

```js preview height=340
export default function render(root, platform) {
  const ui = platform.ui;
  const list = ui.list([]);
  const hint = ui.hint("选择一个分组。");
  function show(id) {
    hint.textContent = id === "open" ? "当前显示未完成事项" : "当前显示已完成事项";
    const row = ui.row(id === "open" ? "取快递" : "回复消息", ui.tag(id === "open" ? "未完成" : "已完成"));
    row.onclick = () => ui.toast("这里可以打开详情页");
    list.replaceChildren(row);
  }
  const tabs = ui.tabs([
    { id: "open", label: "未完成" },
    { id: "done", label: "已完成" },
  ], { value: "open", onChange: show });
  root.replaceChildren(ui.page([ui.card(ui.stack([
    ui.title("待办"), tabs, hint, list,
    ui.button("新增事项", () => ui.toast("这里可以打开新增表单"), "secondary"),
  ]))]));
  show("open");
}
```

`tabs` 只管理选中按钮，不会创建页面或自动筛选数据。首次渲染不会调用 `onChange`，因此应用需要自己加载初始内容。

`row` 本身是按钮，不要在它内部嵌套另一个按钮、输入框或链接。需要多操作行时，使用自定义容器组织控件。

一行需要显示不止一段文字（比如标题+副标题两行）时，不要把拼好的节点传给 `label`。先 `const row = ui.row("")` 拿到空按钮，再 `row.replaceChildren(yourNode)` 替换内容：

```js
const row = ui.row("");
const main = document.createElement("div");
main.append(ui.title(item.name), ui.muted(item.note));
row.replaceChildren(main);
```

写入期间可以设置 `button.disabled = true`，在请求结束后恢复，避免重复操作。
