aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/FormExample.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/FormExample.tsx')
-rw-r--r--src/FormExample.tsx309
1 files changed, 0 insertions, 309 deletions
diff --git a/src/FormExample.tsx b/src/FormExample.tsx
deleted file mode 100644
index 631bf53..0000000
--- a/src/FormExample.tsx
+++ /dev/null
@@ -1,309 +0,0 @@
-import { useState } from 'react'
-import {
- Alert,
- Button,
- Checkbox,
- Cascader,
- Code,
- ColorInput,
- Container,
- 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<string | null>(null)
- const withColumns = useMatches({ base: false, sm: true })
- const form = useForm<FormValues>({
- 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 (
- <Container size="xl" px={0}>
- <Stack gap="lg">
- <div>
- <Title order={1}>表单示例</Title>
- <Text c="dimmed">体验常用表单控件。提交后在本页预览结果,数据和文件不会发送到服务器。</Text>
- </div>
-
- <Paper withBorder p="md">
- <form
- onSubmit={form.onSubmit(submit, () => setSubmitted(null))}
- onReset={(event) => {
- event.preventDefault()
- form.reset()
- form.setErrors(exampleErrors)
- setSubmitted(null)
- }}
- noValidate
- >
- <Stack gap="lg">
- <Text size="sm" c="dimmed">带 * 的项目为必填项;离开输入框或提交时会校验。邮箱预填了错误值,用于演示错误提示。</Text>
- <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
- <TextInput
- label="文本输入框"
- placeholder="请输入名称"
- required
- {...form.getInputProps('name')}
- />
- <TextInput
- label="邮箱(错误提示示例)"
- description="修改为有效邮箱后可正常提交"
- placeholder="name@example.com"
- type="email"
- autoComplete="email"
- required
- {...form.getInputProps('email')}
- />
- <Select
- label="选择框"
- placeholder="请选择角色"
- data={['开发者', '设计师', '产品经理']}
- searchable
- clearable
- required
- {...form.getInputProps('role')}
- />
- <NumberInput
- label="数值输入框"
- description="数量,范围 0–100"
- min={0}
- max={100}
- allowDecimal={false}
- {...form.getInputProps('quantity')}
- />
- <PasswordInput
- label="密码输入框"
- description="选填,填写时至少 8 位"
- placeholder="请输入示例密码"
- autoComplete="new-password"
- {...form.getInputProps('password')}
- />
- </SimpleGrid>
-
- <Cascader
- label="级联选择框"
- description="选择省、市、区,使用部分示例地区"
- placeholder="请选择省 / 市 / 区"
- data={regionOptions}
- withColumns={withColumns}
- searchable
- clearable
- closeOnSelect
- clearButtonProps={{ 'aria-label': '清空地区' }}
- nothingFoundMessage="没有匹配的地区"
- {...form.getInputProps('region')}
- />
-
- <SimpleGrid cols={{ base: 1, sm: 2 }} spacing="lg">
- <MultiSelect
- label="下拉多选框"
- description="选择多个技术方向"
- placeholder="请选择技术方向"
- data={['React', 'TypeScript', 'Vite', 'Mantine', 'Node.js']}
- searchable
- clearable
- clearButtonProps={{ 'aria-label': '清空技术方向' }}
- nothingFoundMessage="没有匹配的选项"
- {...form.getInputProps('technologies')}
- />
- <TagsInput
- label="标签输入框"
- description="输入后按 Enter 或逗号添加标签,也可选择推荐标签"
- placeholder="输入或选择标签"
- data={['学习', '工作', '个人项目', '开源']}
- clearable
- clearButtonProps={{ 'aria-label': '清空标签' }}
- {...form.getInputProps('tags')}
- />
- <Radio.Group label="单选框" description="偏好的联系方式" {...form.getInputProps('contact')}>
- <Group mt="xs">
- <Radio value="email" label="邮件" />
- <Radio value="phone" label="电话" />
- <Radio value="message" label="站内消息" />
- </Group>
- </Radio.Group>
- <Checkbox.Group label="多选框" description="感兴趣的方向" {...form.getInputProps('interests')}>
- <Group mt="xs">
- <Checkbox value="frontend" label="前端" />
- <Checkbox value="backend" label="后端" />
- <Checkbox value="design" label="设计" />
- </Group>
- </Checkbox.Group>
- <ColorInput
- label="颜色选择框"
- format="hex"
- swatches={['#228be6', '#40c057', '#fab005', '#fa5252', '#7950f2']}
- {...form.getInputProps('color')}
- />
- <FileInput
- label="文件上传框"
- description="支持选择多个文件,仅展示文件信息"
- placeholder="点击选择文件"
- leftSection={<UploadSimpleIcon aria-hidden="true" />}
- multiple
- clearable
- {...form.getInputProps('files')}
- />
- </SimpleGrid>
-
- {form.values.files.length > 0 && (
- <Stack gap="xs" aria-live="polite">
- {form.values.files.map((file, index) => (
- <Text key={`${file.name}-${index}`} size="sm" className={classes.fileName}>
- {file.name} · {(file.size / 1024).toFixed(1)} KB
- </Text>
- ))}
- </Stack>
- )}
-
- <Textarea
- label="文本框"
- description="多行文本,例如个人介绍或备注"
- placeholder="请输入内容"
- autosize
- minRows={3}
- maxRows={8}
- {...form.getInputProps('description')}
- />
- <Switch
- label="开关:接收消息通知"
- {...form.getInputProps('notifications', { type: 'checkbox' })}
- />
- <Stack gap="xs">
- <details>
- <summary>查看示例协议</summary>
- <Text size="sm" c="dimmed" py="xs">
- 本页面仅用于演示表单交互。提交内容仅在当前页面预览,文件不会上传。
- 勾选同意用于演示提交前的校验流程,不代表订购或开通服务。
- </Text>
- </details>
- <Checkbox
- label="我已阅读并同意示例协议"
- required
- {...form.getInputProps('acceptedTerms', { type: 'checkbox', withError: true })}
- />
- </Stack>
- <Group>
- <Button type="submit">提交预览</Button>
- <Button type="reset" variant="default">重置</Button>
- </Group>
- </Stack>
- </form>
- </Paper>
-
- {submitted !== null && (
- <Stack gap="xs">
- <Alert title="提交成功" color="green" role="status">以下是本次提交的示例数据。</Alert>
- <Code block>{submitted}</Code>
- </Stack>
- )}
- </Stack>
- </Container>
- )
-}