前言
最近在用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>
就先记录到这里,后续更新追记。感兴趣可以仓库看源码;