# 表单控件

控件返回 DOM 节点，通过属性读取值，通过原生事件响应编辑。为每个字段提供清楚的文字标签，不要只依赖 placeholder。

| 方法 | 返回值与参数 |
| --- | --- |
| `field(input)` | 包装一个节点的 div 容器，提供输入区域样式。 |
| `input(attrs = {})` | HTMLInputElement；`attrs` 赋给 DOM 属性，例如 `type`、`value`、`placeholder`、`disabled`。 |
| `textarea(attrs = {})` | HTMLTextAreaElement；未指定行数时通常使用浏览器默认行数，可显式提供 `rows`。 |
| `select(options = [], attrs = {})` | HTMLSelectElement；选项是字符串或 `{ value, label }`。 |
| `checkbox(label, attrs = {})` | HTMLLabelElement，内部包含 checkbox input。通过 `querySelector("input")` 读取 `checked`。 |
| `switch(checked, onChange)` | button，带 switch 角色。点击切换状态，并将新的 boolean 传给回调。 |

```js preview height=440
export default function render(root, platform) {
  const ui = platform.ui;
  const title = ui.input({ placeholder: "例如 买菜", value: "" });
  title.setAttribute("aria-label", "事项名称");
  const detail = ui.textarea({ placeholder: "补充说明", rows: 3 });
  detail.setAttribute("aria-label", "补充说明");
  const priority = ui.select([
    { value: "normal", label: "普通" },
    { value: "high", label: "重要" },
  ]);
  priority.value = "normal";
  priority.setAttribute("aria-label", "优先级");
  const done = ui.checkbox("已经完成", { checked: false });
  root.replaceChildren(ui.page([ui.card(ui.stack([
    ui.title("新增事项"),
    ui.field(title),
    ui.field(detail),
    priority,
    done,
    ui.button("查看填写内容", () => {
      const checked = done.querySelector("input").checked;
      ui.toast((title.value.trim() || "未填写事项") + (checked ? " · 已完成" : " · 未完成"));
    }),
  ]))]));
}
```

## 选择与事件

`select` 先创建选择框，再添加选项。需要可靠的初始选中值时，在返回节点上设置 `select.value`，例如上面的示例。

`input` 和 `textarea` 可以监听 `input`；`select` 和 checkbox 可以监听 `change`。日期输入使用 `input({ type: "date" })`，读取金额后使用 `Number(input.value)` 转换为数字，再写入数据库。

`switch` 自己切换视觉状态，但不会保存数据。需要持久化时，在 `onChange` 中调用数据 API，并处理写入失败。
