diff options
Diffstat (limited to 'src/FormExample.tsx')
| -rw-r--r-- | src/FormExample.tsx | 309 |
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> - ) -} |
