---
url: /docs/frontend/forms.md
---
# 表单与校验

## 三层校验

输入框先提供格式提示，提交前用 Zod 校验，服务端再次校验。客户端校验改善体验；身份、权限、价格、积分和资源归属必须由服务端判断。

登录组件位于 `apps/app/components/auth/`，账号表单遵循认证配置返回的可用方式，不能自行显示一个服务端未启用的入口。

## 表单状态

表单应区分空白、输入错误、提交中、服务端错误和成功。提交中禁用重复提交，保留用户输入；失败后显示可理解的说明并提供重试。不要将原始错误堆栈或密钥暴露给用户。

```ts
import { z } from "zod";

const organizationInput = z.object({
  name: z.string().trim().min(1).max(100),
});

const result = organizationInput.safeParse({ name: formName });
if (!result.success) {
  // 将字段错误显示在对应输入框旁
  return;
}
```

这是校验示例，长度上限应与实际接口和数据库要求保持一致。

## 字段错误与访问性

给输入框绑定可见标签，使用 `aria-describedby` 关联帮助和错误说明，错误字段设置 `aria-invalid`。成功后的跳转应在接口确实成功后发生；组织创建后还需要刷新会话，等待活动组织写入。

tRPC 的字段校验错误通过 `error.data.zodError` 读取；统一错误状态工具位于 `apps/app/lib/errors.ts`。验证码、密码重置和通行密钥失败流程见 [账号认证](../auth/index.md)。

## 排查

先看提交请求是否发出、请求输入是否符合契约，再查字段错误与网络错误是否分开处理。提交按钮卡住时检查是否遗漏结束状态，或等待会话刷新失败后没有显示错误。
