前言
最近在用vant做关于移动端的项目,由于表单字段太多,不想写直接写到template中,这样太繁琐了,所以我们以把表单弄成schema配置形式:
// data.ts
import type { ItemSchema } from '@/typing/helper';
import { StudentField } from '@/components';
import { getDictTextByCode } from '@/data/dict';import {textField,switchField,dividerField,dictField,selectField,entityField,
} from '@/helpers';export const baseFormSchema: ItemSchema[] = [dividerField('普通文本'),textField('name', '姓名', { required: true }),dividerField('文本域'),textField('address', '地址', { type: 'textarea' }),dividerField('是否字段'),switchField('isMarried', '是否婚配'),dividerField('字典选择'),dictField('marital', '婚姻情况', 'marital'),dividerField('select'),selectField('way', '交通工具', [{ text: '汽车', code: 1 },{ text: '步行', code: 2 },]),dividerField('实体选择(学生)'),entityField('studentId', '学生', StudentField, {change(record) {console.log(record);},}),
];
form组件使用:
<template><div class="content"><code><pre>{{ formValue }}</pre></code><TwiceForm :form-schema="baseFormSchema" v-model="formValue"></TwiceForm></div>
</template><script setup lang="ts">import { ref } from 'vue';import { TwiceForm } from '@/components';import { baseFormSchema } from '@/views/form/data';const formValue = ref({name: '',age: '',address: '',isMarried: true,marital: -1,way: 1,studentId: '',});
</script>
<style lang="less" scoped></style>
就是通过数据驱动生成表单(效果如下)。
也可以戳链接亲自体验:vant_twice_form
小成果
这样不仅表单字段配置起来方便,更能解决一些业务交互上的一些需求:
比如根据一个字段值判断是否需要填写后续字段
又比如选实体字段时把带回来的记录显示到表单中:
下面就看看我是怎么做的吧。
实现步骤(以文本字段为例):
- 根据字段类型封装生成该类型schema的方法
import { merge } from 'lodash-es';
import type { Props, ItemSchema } from '@/typing/helper';
import { Field } from 'vant';/** * 文本字段* @param field* @param label* @param props*/
export function textField(field: string,label: string,props?: Props
): ItemSchema {return {component: Field,props: merge({field,label,placeholder: `请输入${label}`,rules: props?.required? [{ required: true, message: `${label}不能为空` }]: [],},props),};
}
- 把生成的schema列表遍历出来
<template><Form ref="formRef"><template v-for="schema in formSchema" :key="schema"><template v-if="schema?.props?.field"><template v-if="calcShow(schema)"><component:is="schema.component"v-bind="schema.props"v-model="formValue[schema.props.field]"@change="(record) =>schema?.props?.change &&schema?.props?.change(record, formValue, formSchema)"/></template></template><component v-else :is="schema.component" v-bind="schema.props" /></template></Form>
</template><script setup lang="ts">import { ref, unref, watch, computed } from 'vue';import { Form } from 'vant';import type { ItemSchema } from '@/typing/helper';const props = defineProps<{formSchema: ItemSchema[];modelValue: any;}>();const formValue = ref(props.modelValue);const formRef = ref();const emits = defineEmits<{(e: 'update:modelValue', params: any): void;(e: 'submit', params: any): void;}>();watch(formValue.value, (value) => {emits('update:modelValue', unref(value));});const calcShow = computed(() => (schema: ItemSchema) => schema?.props?.show === false ? false : true);defineExpose({// 表单校验validate: async () => await formRef.value.validate(),// 设置值(清空、回显)setValue: function <T>(value: T) {formValue.value = value;},});
</script>
<style lang="less" scoped></style>
- 配置schema
import {textField,dividerField,
} from '@/helpers';export const baseFormSchema: ItemSchema[] = [dividerField('普通文本'),textField('name', '姓名', { required: true }),
]
- 使用form组件
<template><div class="content"><code><pre>{{ formValue }}</pre></code><TwiceForm :form-schema="baseFormSchema" v-model="formValue"></TwiceForm></div>
</template><script setup lang="ts">import { ref } from 'vue';import { TwiceForm } from '@/components';import { baseFormSchema } from '@/views/form/data';const formValue = ref({name: '',age: '',address: '',isMarried: true,marital: -1,way: 1,studentId: '',});
</script>
<style lang="less" scoped></style>
就先记录到这里,后续更新追记。感兴趣可以仓库看源码;