在平常开发中,form表单是写的最多且最繁琐的一个功能。为了避免代码冗余,我把form表单抽出来当做一个组件并且根据数据来动态生成基本表单
现在只写了input和select 后续会加上更多,且优化双向绑定的代码
用到的技术
1.Vue3基础知识
2.Element-plus组件
定义formTemplate
判断是否需要布局(el-col)
判断是否需要form包裹(el-form-item)
根据传递的参数来生成指定组件(components):比如输入框 下拉框等
注意:使用的时候需要全局注册一下指定组件 具体看 倒数第二步 全局注册组件
formTemplate.vue
<template>
<el-col v-if="isCol" :span="colSpan">
<div v-if="isForm">
<el-form-item :prop="prop" :label="label">
<component
:is="isCom"
:config="config"
v-model="templateValue"
@change="change"
/>
</el-form-item>
</div>
<div v-else>
<component
:is="isCom"
:config="config"
v-model="templateValue"
@change="change"
/>
</div>
</el-col>
<div v-else>
<div v-if="isForm">
<el-form-item :prop="prop" :label="label">
<component
:is="isCom"
:config="config"
v-model="templateValue"
@change="change"
/>
</el-form-item>
</div>
<div v-else>
<component
:is="isCom"
:config="config"
v-model="templateValue"
@change="change"
/>
</div>
</div>
</template>
<script setup>
import { watch, ref } from "vue";
/**
* @author BlackKey
* @params modelValue 双向绑定值
* @params colSpan 布局占位
* @params isCol 是否启用布局
* @params isForm 是否用form包裹
* @params label 标题
* @params prop model的键名
* @params isCom 需要的生成的组件
* @params config 组件对应的配置文件
* @desc form表单默认格式
*/
const { colSpan, isCol, isForm, label, prop, isCom, modelValue, config } =
defineProps({
colSpan: {
type: Number,
default: 6,
},
isCol: {
type: Boolean,
default: true,
},
isForm: {
type: Boolean,
default: true,
},
label: {
type: String,
default: "",
},
prop: {
type: String,
default: "",
},
isCom: {
type: String,
default: "wx-input",
},
modelValue: {
type: [String, Number, Object, Array],
default: "",
},
config: {
type: Object,
default: () => ({}),
},
});
const templateValue = ref(modelValue);
const emit = defineEmits(["change"]);
const change = (data) => {
emit("change", data);
};
</script>
<style lang="less" scoped></style>
默认输入框组件
wxInput.vue
<template>
<el-input
v-model="inputValue"
:placeholder="placeholder"
:clearable="clearable"
:disabled="disabled"
:type="type"
:maxlength="maxlength"
:autosize="autosize"
/>
</template>
<script setup>
import { watch, ref } from "vue";
/**
* @author BlackKey
* @params modelValue 双向绑定值
* @params placeholder 输入框占位符文本
* @params type 输入框类型
* @params maxlength 最大长度
* @params disabled 是否禁用
* @params size 输入框尺寸
* @params autosize textarea 高度是否自适应,仅 type 为 'textarea' 时生效。
* @params clearable 是否展示清空按钮
* @desc 输入框
*/
const { config, modelValue } = defineProps({
config: {
type: Object,
default: () => ({}),
},
modelValue: {
type: [String, Number, Object, Array],
default: "",
},
});
const {
placeholder = "",
type = "text",
maxlength = "",
disabled = false,
size = "",
autosize = false,
clearable = true,
} = config;
const emit = defineEmits(["change"]);
const inputValue = ref(modelValue);
watch(
() => inputValue.value,
(data) => {
change(data);
}
);
const change = (data) => {
emit("change", data);
};
</script>
默认下拉框组件
为了防止枚举写一遍 options又写一遍的情况 我把枚举值引入进来了 (baseSelectSource)
如果枚举写了,就直接传dataType即可
meta.js
const sex = [
{
label: "男",
value: "man",
},
{
label: "女",
value: "woman",
},
];
export const baseSelectSource = {
sex,
};
wxSelect.vue
<template>
<el-select
v-model="selectValue"
:clearable="clearable"
:placeholder="placeholder"
:disabled="disabled"
:size="size"
>
<el-option
v-for="item of (options.length && options) || source || []"
:label="item[label]"
:value="item[value]"
></el-option>
</el-select>
</template>
<script setup>
import { watch, ref } from "vue";
import { baseSelectSource } from "@/meta";
/**
* @author BlackKey
* @params modelValue 双向绑定值
* @params placeholder 输入框占位符文本
* @params disabled 是否禁用
* @params size 输入框尺寸
* @params clearable 是否展示清空按钮
* @params options 下拉框的值
* @params dataType 预先设定好的枚举
* @params labelKey 下拉框labelKey
* @params valueKey 下拉框valueKey
* @desc 下拉框
*/
const { config, modelValue } = defineProps({
config: {
type: Object,
default: () => ({}),
},
modelValue: {
type: [String, Number, Object, Array],
default: "",
},
});
const {
placeholder = "",
disabled = false,
size = "",
clearable = true,
options = [],
dataType = "",
labelKey = "label",
valueKet = "value",
} = config;
// 如果dateType有值 代表下拉框取枚举值 从meta里面获取数组用来展示下拉框
const source = ref([]);
if (dataType) {
source.value = baseSelectSource[dataType];
}
const emit = defineEmits(["change"]);
// 接收labelKey和valueKey且绑定
const label = ref(labelKey);
const value = ref(valueKet);
const selectValue = ref(modelValue);
watch(
() => selectValue.value,
(data) => {
change(data);
}
);
const change = (data) => {
emit("change", data);
};
</script>
全局注册组件
使用该组件的时候如果不全局注册需要的组件 这样会导致生成失败
registerComponent.js
import FormTemplate from "@/components/FormTemplate/index.vue";
import wxInput from "@/components/FormTemplate/components/wxInput.vue";
import wxSelect from "@/components/FormTemplate/components/wxSelect.vue";
export default function install(app) {
app.component("wx-form", FormTemplate);
app.component("wx-input", wxInput);
app.component("wx-select", wxSelect);
}
注意:记得在main里面引入registerComponent.js文件 否则注册失败
main.js
import { createApp } from "vue";
import App from "./App.vue";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import "@/assets/css/index.less";
import registerComponent from "./registerComponent";
const app = createApp(App);
app.use(registerComponent);
app.use(ElementPlus);
app.mount("#app");
使用方法
index.vue
注意:使用时记得用el-form和el-row包裹一下 因为我设置的默认使用布局和form包裹住
<template>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row>
<wx-form
v-for="item in formTemplate"
:label="item.label"
:config="item.config"
:isCom="item.isCom"
@change="item.modelValue = $event"
v-model="item.modelValue"
></wx-form>
<el-col :span="24">
<div class="btn">
<el-button type="primary" @click="search">查询</el-button>
</div>
</el-col>
</el-row>
</el-form>
</template>
<script setup>
import { reactive, toRefs, ref } from "vue";
const userData = reactive({
form: { name: "", account: "", tel: "", sex: "" },
});
const { form } = { ...toRefs(userData) };
const formTemplate = ref([
{
label: "昵称",
modelValue: form.value.name,
prop: "name",
config: {
placeholder: "请输入昵称",
},
},
{
label: "账号",
modelValue: form.value.account,
prop: "account",
config: {
placeholder: "请输入账号",
},
},
{
label: "手机",
modelValue: form.value.tel,
prop: "tel",
config: {
placeholder: "请输入手机",
},
},
{
label: "性别",
modelValue: form.value.sex,
isCom: "wx-select",
prop: "sex",
config: {
placeholder: "请选择性别",
dataType: "sex",
},
},
]);
// 查询
const search = () => {
formTemplate.value.forEach((val) => {
form.value[val.prop] = val.modelValue;
});
console.log(form.value);
};
</script>
<style lang="less" scoped>
.btn {
text-align: right;
}
</style>