From fb14fbe0a3ecc801f010a7a429e30ddc57d9f2e4 Mon Sep 17 00:00:00 2001 From: shu Date: Sun, 6 Sep 2026 17:39:46 +0800 Subject: 新增 Mantine 表单示例页面及输入校验演示 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 /form 路由和表单示例菜单,通过路由懒加载拆分页面资源 - 展示文本、密码、数值、多行文本、单选、复选、开关、颜色和文件选择控件 - 使用 Cascader 展示省市区级联选择,支持搜索、清空和移动端路径列表 - 添加 MultiSelect 下拉多选与 TagsInput 自定义标签输入 - 接入 Mantine 表单状态、提交预览和重置,预览隐藏密码并仅显示文件元信息 - 添加必填标识、邮箱错误状态与提示,以及示例协议阅读和同意校验 - 将文件名换行样式封装为 CSS Module 验证:pnpm build、pnpm lint、git diff --check 均通过。 --- src/FormExample.module.css | 3 + src/FormExample.tsx | 306 +++++++++++++++++++++++++++++++++++++++++++++ src/routes.ts | 8 +- 3 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 src/FormExample.module.css create mode 100644 src/FormExample.tsx (limited to 'src') diff --git a/src/FormExample.module.css b/src/FormExample.module.css new file mode 100644 index 0000000..913f3f9 --- /dev/null +++ b/src/FormExample.module.css @@ -0,0 +1,3 @@ +.fileName { + overflow-wrap: anywhere; +} diff --git a/src/FormExample.tsx b/src/FormExample.tsx new file mode 100644 index 0000000..7f38754 --- /dev/null +++ b/src/FormExample.tsx @@ -0,0 +1,306 @@ +import { useState } from 'react' +import { + Alert, + Button, + Checkbox, + Cascader, + Code, + ColorInput, + FileInput, + Group, + MultiSelect, + NumberInput, + Paper, + PasswordInput, + Radio, + Select, + SimpleGrid, + Stack, + Switch, + TagsInput, + Text, + Textarea, + TextInput, + Title, + useMatches, +} from '@mantine/core' +import { isEmail, useForm } from '@mantine/form' +import { UploadSimpleIcon } from '@phosphor-icons/react' +import classes from './FormExample.module.css' + +// 仅用于演示联动,不是完整行政区划数据。 +const regions = [ + { + name: '浙江省', + cities: [ + { name: '杭州市', districts: ['西湖区', '拱墅区'] }, + { name: '宁波市', districts: ['海曙区', '鄞州区'] }, + ], + }, + { + name: '江苏省', + cities: [ + { name: '南京市', districts: ['鼓楼区', '玄武区'] }, + { name: '苏州市', districts: ['姑苏区', '吴中区'] }, + ], + }, +] + +const regionOptions = regions.map((province) => ({ + value: province.name, + children: province.cities.map((city) => ({ + value: city.name, + children: city.districts.map((district) => ({ value: district })), + })), +})) + +type FormValues = { + name: string + email: string + role: string | null + region: string[] | null + contact: string + interests: string[] + technologies: string[] + tags: string[] + color: string + files: File[] + quantity: number | string + password: string + description: string + notifications: boolean + acceptedTerms: boolean +} + +const exampleErrors = { email: '邮箱格式不正确,例如 name@example.com' } + +export default function FormExample() { + const [submitted, setSubmitted] = useState(null) + const withColumns = useMatches({ base: false, sm: true }) + const form = useForm({ + mode: 'controlled', + validateInputOnBlur: true, + initialErrors: exampleErrors, + initialValues: { + name: '', + email: 'invalid-email', + role: null, + region: null, + contact: 'email', + interests: [], + technologies: [], + tags: [], + color: '#228be6', + files: [], + quantity: 1, + password: '', + description: '', + notifications: false, + acceptedTerms: false, + }, + validate: { + name: (value) => value.trim() ? null : '请输入名称', + email: (value) => !value.trim() ? '请输入邮箱' : isEmail(exampleErrors.email)(value.trim()), + role: (value) => value ? null : '请选择角色', + quantity: (value) => typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 100 + ? null : '请输入 0–100 之间的整数', + password: (value) => value && value.length < 8 ? '密码至少需要 8 位' : null, + color: (value) => /^#[\da-f]{6}$/i.test(value) ? null : '请输入有效的六位 HEX 颜色', + acceptedTerms: (value) => value ? null : '请先阅读并同意示例协议', + }, + }) + + function submit(values: FormValues) { + setSubmitted(JSON.stringify({ + ...values, + password: values.password ? '已填写(不展示密码)' : '未填写', + files: values.files.map((file) => ({ + name: file.name, + size: file.size, + type: file.type || '未知类型', + })), + }, null, 2)) + } + + return ( + +
+ 表单示例 + 体验常用表单控件。提交后在本页预览结果,数据和文件不会发送到服务器。 +
+ + +
setSubmitted(null))} + onReset={(event) => { + event.preventDefault() + form.reset() + form.setErrors(exampleErrors) + setSubmitted(null) + }} + noValidate + > + + 带 * 的项目为必填项;离开输入框或提交时会校验。邮箱预填了错误值,用于演示错误提示。 + + + +