简单记录一下vue中Form与Form.Item的实现

355 阅读1分钟

Form表单的实现原理

原理非常简单:基于vue中的v-model,通过传入的值和规则进行匹配和处理

先看想要实现的功能

这边只谈最常用的提交,重置,校验

<script setup>
import Form from '@/components/Form/index.vue'
import FormItem from '@/components/FormItem/index.vue'
import ComInput from '@/components/ComInput/index.vue'
import { ref } from 'vue';

const formRef = ref(null)
const formData = ref({
  text: '',
})

// 规则
const rules = {
  text: {
    required: true,
    message: '请输入申请原因',
    validateField: (value) => {
      if (value.length < 10) {
        return '申请原因不能少于10个字'
      } else {
        return false
      }
    }
  },
}

// 提交
const submit = () => {
  const res = formRef.value.validateForm()
}

// 重置
const reset = () => {
  formRef.value.resetForm()
}

</script>

<template>
  <Form ref="formRef" v-model="formData" :rules="rules">
    <FormItem label="文本" name="text" required>
      <ComInput placeholder="请输入文本" v-model="formData.text" />
    </FormItem>
  </Form>

  <button @click="submit">提交</button>    
  <button @click="reset">重置</button>
</template>

Form组件的实现

<script lang="ts" setup>
import { ref, provide, PropType, onMounted } from 'vue'

const props = defineProps({
  modelValue: {
    type: Object,
    default: () => ({})
  },
  rules: {
    type: Object as PropType<{
      [key: string]: {
        required?: boolean;
        message?: string;
        validateField?: any;
      }
    }>,
    default: () => ({})
  }
})

// 用于存储formitem的一系列方法
const formItems = ref<any>({});
// 用于存放初始化的值
const initForm = ref<any>({});
const emit = defineEmits(['update:modelValue'])

// 要将这个函数通过provide传递给FormItem组件
const setFormItemList = (name: string, options: any) => {
  formItems.value[name] = options
};

// 实现校验功能
const validateForm = () => {
  // 这边用来判断FormItem上的required标志
  Object.values(formItems.value).forEach((item: any) => {
    if (item.required && !props.modelValue[item.name]) {
      alert('不能为空')
      return false
    }
  })
  // 这边用来判断rules
  for(let key in props.modelValue){
    if (props.rules[key]) {
      if (props.rules[key].required && !props.modelValue[key]) {
        uni.showToast({
          title: props.rules[key].message,
          icon: 'none'
        })
        return false
      }
      if (props.rules[key].validateField) {
        const res = props.rules[key].validateField(props.modelValue[key])
        if (res) {
          uni.showToast({
            title: res,
            icon: 'none'
          })
          return false
        }
      }
    }
  }
};

// 重置为初始化
const resetForm = () => {
  emit('update:modelValue', initForm.value) 
}

// 传递给FormItem
provide('setFormItemList', setFormItemList)

// 抛出方法
defineExpose({
  validateForm,
  resetForm
})

// 将初始化的值设置进去
onMounted(() => {
  initForm.value = JSON.parse(JSON.stringify(props.modelValue))
})

</script>
<template>
  <slot></slot>
</template>

FormItem的设置

<script lang='ts' setup>
import { inject, ref, onMounted } from 'vue';

const props = defineProps({
  name: {
    type: String,
    required: true
  },
  label: {
    type: String,
    required: true
  },
  required: {
    type: Boolean,
    default: false
  },
})

const setFormItemList:any = inject('setFormItemList')

onMounted(() => {
  setFormItemList(props.name, {
    name: props.name,
    label: props.label,
    required: props.required
  })
})
</script>

以上就是基本设置,还有一些清理校验,或将校验字段设置在输入框底下之类的,都可以基于这个模板继续改造