feat: ZodForm
All checks were successful
/ surge (push) Successful in 44s

This commit is contained in:
严浩
2024-11-20 16:24:48 +08:00
parent c9c4d523c1
commit 998cb5d4e3
7 changed files with 382 additions and 266 deletions

View File

@ -0,0 +1,77 @@
<script setup lang="ts">
import { FormKitMessages } from '@formkit/vue'
</script>
<template>
<FormKit
type="list"
name="guests"
label="客人列表"
:value="[{}]"
dynamic
validation="required"
#default="{ items, node, value }"
>
<div
class="attributes-group"
v-auto-animate
>
<FormKitMessages />
<FormKit
type="group"
v-for="(item, index) in items"
:key="item"
:index="index"
>
<div class="list-group-item">
<FormKit
type="text"
name="name"
label="客人姓名"
placeholder="客人姓名"
validation="required"
value="默认值"
>
</FormKit>
<FormKit
type="number"
name="age"
label="客人年龄"
/>
<button
type="button"
class="border border-blue-600 text-blue-600 p-3"
@click="() => node.input(value?.filter((_, i) => i !== index))"
>
- 移除
</button>
</div>
</FormKit>
<button
type="button"
@click="() => node.input(value?.concat({}))"
class="border border-blue-600 text-blue-600 p-3 mb-4"
>
+ 添加另一个
</button>
<pre wrap>{{ value }}</pre>
</div>
</FormKit>
</template>
<style scoped>
.list-group-item {
position: relative;
padding: 10px;
border: 1px solid grey;
border-radius: 10px;
margin-bottom: 5px;
}
.list-group-item button {
position: absolute;
top: 10px;
right: 10px;
}
</style>

240
src/tutorial-form/index.vue Normal file
View File

@ -0,0 +1,240 @@
<script setup lang="ts">
import { FormKitSchemaDefinition, getNode, type FormKitNode } from '@formkit/core';
import { onMounted } from 'vue';
import Guests from './guests.vue';
async function submit(...args: any[]) {
console.debug('submit', `args :>> `, args);
await new Promise(r => setTimeout(r, 1000))
alert('Submitted! 🎉')
}
const castNumber = (node: FormKitNode) => {
node.hook.input((value, next) => next(Number(value)))
}
onMounted(() => {
console.group('onMounted')
const nameNode = getNode('nameNodeId') // 用 id 获取节点
Object.assign(window, { nameNode })
console.debug(`nameNode :>> `, nameNode);
const schemaGroupInput = nameNode?.root.at('schemaGroup.input') // 用路径获取节点
console.debug(`schemaGroupInput :>> `, schemaGroupInput);
nameNode?.on('commit', ({ payload }) => {
console.debug('[nameNode] [on commit]', `payload :>> `, payload);
schemaGroupInput?.input(`姓名:${payload}`)
})
console.groupEnd()
})
const SCHEMA: FormKitSchemaDefinition = [
{
$el: 'h1',
children: [
{
$el: 'span',
children: 'JSON Schema Form',
},
],
attrs: {
class: 'font-bold text-2xl mb-4',
'data-foo': 'bar',
},
},
{
$formkit: 'group',
name: 'schemaGroup',
children: [
{
$formkit: 'text',
label: 'JSON Schema 渲染的输入框',
name: 'input',
validation: 'required',
validationLabel: 'validationLabel', // validation message 使用顺序validationLabel > label > name
errors: ['显式设置的错误是非阻塞的,这意味着它们不会像验证错误那样阻止表单提交。您可以在表单文档中了解更多有关错误处理的信息。']
}
]
}
];
function onChangeName() {
const nameNode = getNode('nameNodeId')
nameNode?.input('测试')
}
import autoAnimate from "@formkit/auto-animate"
onMounted(() => {
// setTimeout(() => {
autoAnimate(document.getElementById('attributes-group-wrapper')!);
// });
})
</script>
<template>
<div class="bg-white rounded-xl shadow-xl p-8 mx-auto my-16 max-w-[450px]">
<h1
class="font-bold text-2xl mb-4"
data-foo="bar"
>Tutorial Form</h1>
<FormKit
type="form"
#default="{ value }"
@submit="submit"
submit-label="提交 "
>
<!-- v-auto-animate -->
<div
class="attributes-group"
id="attributes-group-wrapper"
>
<FormKit
type="group"
name="attributes"
:validation-rules="{
validateGroup: (node) => {
const name = (node.value as Record<string, any>).name
return name?.includes('测试') ? true : false
}
}"
validation-visibility="dirty"
validation="validateGroup"
:validation-messages="{
validateGroup: ({ name/* , args */ }) => {
return `分组 ${name} 下的姓名必须包含 '测试' 字符串。`
},
}"
#default="{ id, messages, fns, classes, value: attributes, }"
>
<FormKit
type="text"
name="name"
id="nameNodeId"
label="姓名"
:help="`现在 confirmName 的值是:${attributes?.confirmName}`"
some-attr="some-attr-value"
>
<template #label="{ classes, attrs, label }">
<!-- <pre
class="font-mono text-sm p-4 bg-slate-100 mb-4">{{ JSON.stringify({ classes, attrs, label }, null, 2) }}</pre> -->
<label
for="nameNodeId"
v-bind="attrs"
:class="classes.label"
>{{ label }}<button
class="ml-2 px-2 py-1 bg-blue-500 text-white rounded"
type="button"
@click="onChangeName"
>点击赋值</button>
</label>
</template>
</FormKit>
<FormKit
v-if="attributes?.name"
:preserve="false"
name="confirmName"
type="checkbox"
:value="false"
:label="`如果清空 name 输入框,这个确认框及其值将被移除。`"
/>
<FormKit
type="checkbox"
name="flavors"
label="最喜欢的冰淇淋口味"
:options="{
'vanilla': '香草',
'chocolate': '巧克力',
'strawberry': '草莓',
}"
validation="required|min:2"
/>
<FormKit
type="range"
name="strength"
id="strength"
label="力量"
value="5"
validation="min:2|max:9"
validation-visibility="live"
min="1"
max="10"
step="1"
help="这个角色应该有多少力量点"
:plugins="[castNumber]"
class="mb-4"
/>
<FormKit
type="textarea"
auto-height
label="我有自动高度插件"
help="这个文本框会随着输入内容增加而增高"
/>
<!-- By default groups do not show validation messages, so we need to add it manually -->
<ul
class="error-box"
:class="classes.messages"
v-if="fns.length(messages)"
>
<li
v-for="message in messages"
:key="message.key"
:id="`${id}-${message.key}`"
:data-message-type="message.type"
>
{{ message.value }}
</li>
</ul>
</FormKit>
</div>
<div class="attributes-group">
<FormKitSchema :schema="SCHEMA" />
</div>
<Guests />
<FormKit
type="checkbox"
name="agree"
label="我同意FormKit是最好的表单开发框架"
class="mb-4"
/>
<pre class="font-mono text-sm p-4 bg-slate-100 mb-4">{{ value }}</pre>
</FormKit>
</div>
</template>
<style scoped>
ul.error-box {
padding: 0.75rem;
border: 1px solid #ef4444;
border-radius: 0.375rem;
background-color: #fee2e2;
color: #b91c1c;
font-size: 0.875rem;
line-height: 1.25rem;
}
ul.error-box p {
font-weight: 500;
}
</style>
<style>
.attributes-group {
padding: 1.5rem;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
margin-bottom: 1rem;
background-color: #f9fafb;
}
</style>