【vant】打开vant表单的正确形式(基于vant表单的二次封装)

前言

最近在用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>

就先记录到这里,后续更新追记。感兴趣可以仓库看源码;