前提
我们公司正在开发一个内部工作管理系统,其中有一个功能是让员工填写日常工作任务。
不同部门和岗位的工作任务不同,所以他们填写的内容也不一样。比如,项目经理需要填写项目进度和预计完成时间,行政人员需要填写处理的事项和耗时。
为了满足这些差异化需求,我们开发了一个可配置的表单组件。使用这个组件,我们只需要在系统中定义好不同岗位需要填写的表单项,就能自动生成相应的表单。
当员工填写表单时,组件会自动保存数据到一个对象中。我们可以监控这个对象,执行后续的业务逻辑。
组件还能根据我们设置的校验规则,自动检查员工的输入是否合法,并显示错误提示。
最后,当员工提交表单时,我们就可以调用组件提供的提交方法,进一步处理数据后提交给后端系统。
通过使用这个可配置的表单组件,我们可以在不同场景中灵活应用,既提高了开发效率,也方便了系统的后续维护。
说干就干,下面是我写组件的逻辑:
组件逻辑
在组件的 <script>
部分,我们定义了以下关键功能:
- 表单数据管理:
- 使用 reactive 函数创建表单数据对象 formData,并与表单项的 v-model 绑定。
- 通过 watch 函数监听 props.formData 的变化,及时更新 formData。
- 表单项类型判断:
- 定义 getInputComponent 函数,根据表单项的 type 属性返回对应的 Vue 组件。
- 表单初始化:
- 遍历 formItems 数组,将 defaultValue 属性设置为表单项的初始值。
- 表单输入验证:
- 定义 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();
// 在此
}