--- title: 组合资料表单 description: 用 Field、Input、Select 和 Button 组合可访问表单,同时把状态留在应用层。 order: 30 toc: - id: complete-example title: 完整示例 - id: validation title: 验证状态 - id: controlled-select title: 受控 Select - id: styling title: 样式覆盖 --- ## 完整示例 {#complete-example} ```tsx import * as React from "react" import { Button } from "@workspace/ui/components/button" import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel, } from "@workspace/ui/components/field" import { Input } from "@workspace/ui/components/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@workspace/ui/components/select" export function ProfileForm() { const [role, setRole] = React.useState("editor") const [error, setError] = React.useState("") function submit(event: React.FormEvent) { event.preventDefault() const data = new FormData(event.currentTarget) setError(data.get("displayName") ? "" : "请输入显示名称") } return (
显示名称 其他成员会看到这个名称。 {error} 角色
) } ``` ## 验证状态 {#validation} UI 包不选择验证库。应用负责产生错误,再把同一状态连接到 `Field data-invalid`、输入控件的 `aria-invalid` 和 `FieldError`。多个 Schema 错误可通过 `FieldError errors={errors}` 传入,它会去重后渲染列表。 ## 受控 Select {#controlled-select} 本地 Select 基于 Base UI,使用 `value` 与 `onValueChange`。`SelectTrigger` 的箭头由组件自己渲染,只需放入 `SelectValue`;不要再添加第二个 Chevron。弹层默认与 trigger 对齐并使用其宽度,可通过 `SelectContent` 的 `side`、`align`、`sideOffset` 与 `alignItemWithTrigger` 调整。 ## 样式覆盖 {#styling} 先使用组件已有的 `variant`、`size` 和结构部件,再用 `className` 处理页面布局。组件内部使用 `cn` 合并 class,因此消费端的 Tailwind class 可以覆盖默认 utility。跨多个页面重复的覆盖应沉淀为新 variant 或复合组件,而不是复制长 class 字符串。