组件
表单 Form
Form:表单元素本身。拦截原生 submit 并把事件交给引擎,同时提供统一的网格和间距。
#基础用法
<script setup lang="ts">
import { Button, Form, FormField, Input, Textarea, useForm } from "@bysages/vue";
import { ref } from "vue";
import { z } from "zod";
const status = ref("");
const form = useForm({
defaultValues: { title: "", abstract: "" },
validators: {
onChange: z.object({
title: z.string().min(1, "The title is required."),
abstract: z.string().min(8, "Write at least 8 characters."),
}),
},
onSubmit: () => {
status.value = "Submitted.";
},
});
</script>
<template>
<Form :form="form" class="w-full">
<FormField name="title" label="Title" hint="One line, no period" required>
<template #default="{ field }">
<Input
:model-value="field.state.value"
placeholder="Title of the piece"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<FormField name="abstract" label="Abstract">
<template #default="{ field }">
<Textarea
:model-value="field.state.value"
placeholder="What the piece says"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<Button type="submit">Submit</Button>
</Form>
<p role="status" class="mt-3 text-sm text-tertiary">
{{ status }}
</p>
</template>
<script setup lang="ts">
import { Button, Form, FormField, Input, Textarea, useForm } from "@bysages/vue";
import { ref } from "vue";
const status = ref("");
const form = useForm({
defaultValues: { title: "", abstract: "" },
onSubmit: () => {
status.value = "Submitted.";
},
});
const titleValidators = {
onChange: ({ value }: { value: string }) => (value ? undefined : "Title is required"),
};
const abstractValidators = {
onChange: ({ value }: { value: string }) => {
if (!value) return "The abstract is required";
if (value.length < 8) return "At least 8 characters";
return undefined;
},
};
</script>
<template>
<Form :form="form" class="w-full">
<FormField
name="title"
label="Title"
hint="One line, no period"
required
:validators="titleValidators"
>
<template #default="{ field }">
<Input
:model-value="field.state.value"
placeholder="Title of the piece"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<FormField name="abstract" label="Abstract" :validators="abstractValidators">
<template #default="{ field }">
<Textarea
:model-value="field.state.value"
placeholder="What the piece says"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<Button type="submit">Submit</Button>
</Form>
<p role="status" class="mt-3 text-sm text-tertiary">
{{ status }}
</p>
</template>
<script setup lang="ts">
import {
Button,
CheckboxGroup,
Form,
FormField,
Input,
Switch,
Textarea,
useForm,
} from "@bysages/vue";
import { ref } from "vue";
import { z } from "zod";
const status = ref("");
const topics = [
{ label: "Typography", value: "typography" },
{ label: "Lighting", value: "lighting" },
{ label: "Motion", value: "motion" },
];
const form = useForm({
defaultValues: {
title: "",
summary: "",
topics: [] as string[],
consent: false,
},
validators: {
onChange: z.object({
title: z.string().min(1, "The title is required."),
summary: z.string().min(8, "Write at least 8 characters."),
topics: z.array(z.string()).min(1, "Pick at least one topic."),
consent: z.boolean().refine((v) => v, "Please accept the terms."),
}),
},
onSubmit: () => {
status.value = "Submitted.";
},
});
</script>
<template>
<Form :form="form" class="w-full">
<FormField name="title" label="Title" required>
<template #default="{ field }">
<Input
:model-value="field.state.value"
placeholder="Title of the piece"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<FormField name="summary" label="Summary" hint="A few sentences">
<template #default="{ field }">
<Textarea
:model-value="field.state.value"
placeholder="What the piece says"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<FormField name="topics" label="Topics" required>
<template #default="{ field }">
<CheckboxGroup
:model-value="field.state.value"
:options="topics"
@update:model-value="field.handleChange"
@blur="field.handleBlur"
/>
</template>
</FormField>
<FormField name="consent">
<template #default="{ field }">
<Switch.Root
:checked="field.state.value"
@update:checked="field.handleChange"
@blur="field.handleBlur"
>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Label>I accept the terms</Switch.Label>
<Switch.HiddenInput />
</Switch.Root>
</template>
</FormField>
<Button type="submit">Submit</Button>
</Form>
<p role="status" class="mt-3 text-sm text-tertiary">
{{ status }}
</p>
</template>
#属性
#Form
Form:表单元素本身。拦截原生 submit 并把事件交给引擎,同时提供统一的网格和间距。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
form | Object as PropType<AnyFormApi> | undefined | useForm 返回的表单引擎实例,管理表单值、校验和提交;可省略——无引擎时退化为布局容器与原生语义 |
#FormField
表单栅格中的字段槽位,包含标签、控件和提示,也显示引擎为该 name 返回的错误;这些内容复用独立 Field 组件的样式。默认插槽接收引擎生成的字段对象(value、handleChange、handleBlur、full state),控件绑定所需的数据都在这里。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
form | Object as PropType<AnyFormApi> | undefined | 覆盖外层 Form 使用的引擎;适合单独组装的字段 |
name | String | 该字段值和错误信息在表单引擎中的键 | |
label | String | undefined | 控件上方的标题 |
hint | String | undefined | 控件下方的提示文字;没有错误时显示 |
required | Boolean | false | 是否为标签添加必填标记 |
invalid | Boolean | false | 是否强制显示无效样式,不依赖表单引擎的校验结果 |
disabled | Boolean | false | 是否禁用该插槽 |