Bootstrap

vue3封装element plus可配置表单组件

前提

我们公司正在开发一个内部工作管理系统,其中有一个功能是让员工填写日常工作任务。

不同部门和岗位的工作任务不同,所以他们填写的内容也不一样。比如,项目经理需要填写项目进度和预计完成时间,行政人员需要填写处理的事项和耗时。

为了满足这些差异化需求,我们开发了一个可配置的表单组件。使用这个组件,我们只需要在系统中定义好不同岗位需要填写的表单项,就能自动生成相应的表单。

当员工填写表单时,组件会自动保存数据到一个对象中。我们可以监控这个对象,执行后续的业务逻辑。

组件还能根据我们设置的校验规则,自动检查员工的输入是否合法,并显示错误提示。

最后,当员工提交表单时,我们就可以调用组件提供的提交方法,进一步处理数据后提交给后端系统。

通过使用这个可配置的表单组件,我们可以在不同场景中灵活应用,既提高了开发效率,也方便了系统的后续维护。

说干就干,下面是我写组件的逻辑:

组件逻辑

在组件的 <script> 部分,我们定义了以下关键功能:

  1. 表单数据管理:
  • 使用 reactive 函数创建表单数据对象 formData,并与表单项的 v-model 绑定。
  • 通过 watch 函数监听 props.formData 的变化,及时更新 formData。
  1. 表单项类型判断:
  • 定义 getInputComponent 函数,根据表单项的 type 属性返回对应的 Vue 组件。
  1. 表单初始化:
  • 遍历 formItems 数组,将 defaultValue 属性设置为表单项的初始值。
  1. 表单输入验证:
  • 定义 handleInput 函数,用于在用户输入时进行正则表达式验证。
    表单数据获取:

通过 defineExpose 暴露 getFormData 方法,供父组件调用获取表单数据。

<template>
  <el-form label-position="top" label-width="auto" :rules="rules" :model="formData" :inline="true" class="oneForm">
    <el-form-item v-for="item in formItems" :key="item.prop" :label="item.label">
      <component
        :is="getInputComponent(item.type)"
        v-model="formData[item.prop]"
        :type="item.inputType"
        :placeholder="`请填写 ${item.label}`"
        :maxlength="item.maxlength"
        :show-password="item.showPassword"
        :value-format="item['value-format']"
        :disabled-date="disabledDate"
        :shortcuts="shortcuts"
        :size="size"
        :rows="item.rows"
        :autosize="item.autosize"
        @input="(value) => handleInput(item.prop, item.pattern)"
        :style="{ width: item.type === 'textarea' ? '100%' : 'auto' }"
      >
        <template v-if="item.type === 'select'">
          <el-option
            v-for="option in item.options"
            :key="option.value"
            :label="option.label"
            :value="option.value"
          ></el-option>
        </template>
      </component>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { reactive, watch, defineExpose } from 'vue';

const props = defineProps({
  formItems: { type: Array, required: true },
  formData: { type: Object, required: true },
  rules: { type: Object, required: false },
  disabledDate: { type: Function, required: false },
  shortcuts: { type: Array, required: false },
  size: { type: String, required: false },
  class: { type: String, required: false },
});

const formData = reactive({ ...props.formData });

watch(
  () => props.formData,
  (newData) => {
    Object.assign(formData, newData);
  },
  { deep: true }
);

const getFormData = () => {
  return formData;
};

defineExpose({ getFormData });

// 新增的初始化操作
props.formItems.forEach((item) => {
  if (item.defaultValue !== undefined) {
    formData[item.prop] = item.defaultValue;
  }
});

const getInputComponent = (type) => {
  switch (type) {
    case 'input':
      return 'el-input';
    case 'date-picker':
      return 'el-date-picker';
    case 'select':
      return 'el-select';
    case 'textarea':
      return 'el-input';
    default:
      return 'el-input';
  }
};

// 新增的正则验证函数
const handleInput = (prop, pattern) => {
  if (pattern) {
    const regex = new RegExp(pattern);
    formData[prop] = formData[prop].replace(/[^0-9]/g, '');
    if (!regex.test(formData[prop])) {
      formData[prop] = formData[prop].slice(0, -1);
    }
  }
};

</script>

<style scoped>
.oneForm {
  width: 100%;
}
:deep(.el-input__wrapper) {
  width: 240px;
}
:deep(.el-input--textarea) {
  width: 100%;
}
</style>

组件的使用

<template>
  <my-form-component
    :form-items="formItems"
    :form-data="formData"
    :rules="rules"
    :disabled-date="disabledDate"
    :shortcuts="shortcuts"
    :size="size"
    ref="formRef"
  ></my-form-component>

  <button @click="submitForm">提交</button>
</template>

<script setup>
// ... (其他代码省略)

const formItems = [
  { prop: 'name', label: '姓名', type: 'input', maxlength: 20 },
  { prop: 'age', label: '年龄', type: 'input', inputType: 'number', pattern: '^[0-9]*$' },
  { prop: 'birthday', label: '出生日期', type: 'date-picker', 'value-format': 'YYYY-MM-DD' },
  { prop: 'gender', label: '性别', type: 'select', options: [
    { label: '男', value: 'male' },
    { label: '女', value: 'female' }
  ]}
];

const formData = reactive({
  name: '',
  age: '',
  birthday: '',
  gender: ''
});

const rules = {
  name: [{ required: true, message: '请输入姓名' }],
  age: [{ required: true, message: '请输入年龄' }],
  birthday: [{ required: true, message: '请选择出生日期' }],
  gender: [{ required: true, message: '请选择性别' }]
};

const disabledDate = (time) => {
  return time.getTime() > Date.now();
};

const shortcuts = [
  {
    text: '今天',
    value: new Date()
  },
  {
    text: '昨天',
    value: () => {
      const date = new Date();
      date.setTime(date.getTime() - 3600 * 1000 * 24);
      return date;
    }
  }
];

const size = 'default';

const formRef = ref(null);

const submitForm = () => {
  formRef.value.getFormData();
  // 在此
}

;