表单解决大量表单的通用处理组件
为了表单解决大量表单的通用处理组件
基础用法
编辑表单(重置表单)
新增表单(重置表单)
HcFormDemo
vue
<script setup>
import { ElButton } from "element-plus";
import { computed, ref } from "vue";
import useForm from "./index.ts";
import HcForm from "./index.vue";
const { options } = useForm()
const formProps = ref({
labelWidth: 100,
labelPosition: "left",
});
const wrapperRow = ref({
gutter: 20,
});
const wrapperCol = ref({
span: 12,
});
const hcFormRef = ref(null);
const modelValue = computed(() => hcFormRef.value?.modelValue);
const { options: options2, initForm: initForm2 } = useForm()
const { resetForm: resetForm2 } = initForm2('edit', { "name": "张三", "age": 24, "color": "#361A9E", "input004": "输入框1", "count": 29, "input006": "option2", "checkbox": ["option2"], "radio": "option2" })
const formProps2 = ref({
labelWidth: 100,
labelPosition: "left",
});
const wrapperRow2 = ref({
gutter: 20,
});
const wrapperCol2 = ref({
span: 12,
});
const hcFormRef2 = ref(null);
const modelValue2 = computed(() => hcFormRef2.value?.modelValue);
const { options: options3, initForm: initForm3 } = useForm()
const { resetForm: resetForm3 } = initForm3('create', { "name": "张三", "age": 24, "color": "#361A9E", "input004": "输入框1", "count": 29, "input006": "option2", "checkbox": ["option2"], "radio": "option2" })
const formProps3 = ref({
labelWidth: 100,
labelPosition: "left",
});
const wrapperRow3 = ref({
gutter: 20,
});
const wrapperCol3 = ref({
span: 12,
});
const hcFormRef3 = ref(null);
const modelValue3 = computed(() => hcFormRef3.value?.modelValue);
// const separateComp = shallowRef(SeparateComp)
</script>
<template>
<div class="w-full">
<div class="demo-item">
<h4 class="demo-title">基础用法</h4>
<HcForm :options="options" ref="hcFormRef" :form-props="formProps" :wrapperRow="wrapperRow"
:wrapper-col="wrapperCol" />
{{ modelValue }}
</div>
<div class="demo-item">
<h4 class="demo-title">编辑表单(重置表单)</h4>
<HcForm :options="options2" ref="hcFormRef2" :form-props="formProps2" :wrapperRow="wrapperRow2"
:wrapper-col="wrapperCol2" />
{{ modelValue2 }}
<div class="flex justify-center my-4">
<el-button type="primary" @click="resetForm2">重置</el-button>
</div>
</div>
<div class="demo-item">
<h4 class="demo-title">新增表单(重置表单)</h4>
<HcForm :options="options3" ref="hcFormRef3" :form-props="formProps3" :wrapperRow="wrapperRow3"
:wrapper-col="wrapperCol3" />
{{ modelValue3 }}
<div class="flex justify-center my-4">
<el-button type="primary" @click="resetForm3">重置</el-button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.demo-item {
@apply p-4 border rounded-md;
.demo-title {
@apply p-2 w-full mb-2;
}
&:not(:last-child) {
@apply mb-4;
}
}
</style>
HCForm
vue
<script setup lang="ts">
import {
ElCheckbox,
ElCheckboxGroup,
ElCol,
ElColorPicker,
ElForm,
ElFormItem,
ElInput,
ElInputNumber,
ElOption,
ElRadio,
ElRadioGroup,
ElRow,
ElSelect,
ElSlider,
ElSwitch,
} from "element-plus";
import {
defineExpose,
defineProps,
PropType,
reactive,
ref,
watchEffect,
} from "vue";
const components = {
number: ElInputNumber,
switch: ElSwitch,
radio: ElRadioGroup,
checkbox: ElCheckboxGroup,
slider: ElSlider,
color: ElColorPicker,
select: ElSelect,
input: ElInput,
};
type optionsType = {
component: keyof typeof components;
props: any;
label: any;
field: string;
value?: string;
rules?: [];
// 分栏标题
isSeparate?: boolean;
icon?: any;
};
const getComponent = (component: keyof typeof components) => {
return components[component];
};
const props = defineProps({
options: { type: Array as PropType<optionsType[]> },
formProps: {
type: Object,
default: () => ({}),
},
wrapperRow: { type: Object, default: () => ({}) },
wrapperCol: { type: Object, default: () => ({}) },
separateComp: {}, // 分栏组件
});
const formEl = ref();
const fromItemEls = ref<any[]>([]);
const modelValue = reactive<any>({});
const renderOptions = ref<optionsType[]>([]);
const rules = reactive<any>({});
watchEffect(() => {
renderOptions.value = props.options;
renderOptions.value.forEach((item: optionsType) => {
modelValue[item.field] = item.value;
if (item.rules) {
rules[item.field] = item.rules;
}
});
});
defineExpose({
modelValue,
formEl: new Proxy(
{},
{
get(target, key) {
return formEl.value?.[key];
},
}
),
fromItemEls,
});
</script>
<template>
<el-form v-bind="{ 'require-asterisk-position': 'right', ...$props.formProps }" ref="formEl" :rules="rules">
<el-row v-if="$props.wrapperRow" v-bind="$props.wrapperRow">
<template v-for="item in renderOptions">
<el-col v-if="item.isSeparate" v-bind="{ span: 24 }">
<template v-if="!$props.separateComp">
<div class="flex text-md bg-teal-400 px-3 py-2 rounded-md mb-2 text-white">
<div class=""></div>
<div class="font-bold">{{ item.label }}</div>
</div>
</template>
<template v-else>
<component :is="$props.separateComp" :label="item?.label" :icon="item?.icon"></component>
</template>
</el-col>
<el-col v-else-if="$props.wrapperCol" v-bind="$props.wrapperCol">
<el-form-item v-bind="item.props" :label="item?.label" :prop="item?.field" ref="fromItemEls">
<component :is="getComponent(item.component)" v-bind="item?.props || {}" v-model="modelValue[item.field]">
<template v-if="item.component === 'select'">
<el-option v-for="(option, index) in item.props.options" :key="index" :label="option.label"
:value="option.value"></el-option>
</template>
<template v-else-if="item.component === 'radio'">
<el-radio v-for="(option, index) in item.props.options" :key="index" :label="option.label"
:value="option.value"></el-radio>
</template>
<template v-else-if="item.component === 'checkbox'">
<el-checkbox v-for="(option, index) in item.props.options" :key="index" :label="option.label"
:value="option.value"></el-checkbox>
</template>
</component>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<style scoped lang="scss" src="./index.css"></style>
useForm
ts
import { ref } from "vue";
const useForm = () => {
const options = ref([
{
isSeparate: true,
label: "基本信息",
icon: "icon",
},
{
label: "姓名",
component: "input",
field: "name",
value: "张三",
props: {
type: "text",
placeholder: "请输入内容",
},
rules: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{ min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
],
},
{
label: "年龄",
component: "number",
field: "age",
props: {
type: "text",
placeholder: "请输入内容",
controls: false,
},
rules: [
{
required: true,
message: "Please input Activity name",
trigger: "blur",
},
{ min: 3, max: 6, message: "Length should be 3 to 6", trigger: "blur" },
],
},
{
isSeparate: true,
label: "内容分类",
icon: "icon",
},
{
label: "颜色",
component: "color",
field: "color",
props: {
type: "text",
placeholder: "请输入内容",
},
},
{
label: "输入框",
component: "input",
field: "input004",
props: {
type: "text",
placeholder: "请输入内容",
},
},
{
label: "滑块",
component: "slider",
field: "count",
props: {
type: "text",
placeholder: "请输入内容",
},
},
{
label: "选项",
component: "select",
field: "input006",
props: {
type: "text",
placeholder: "请输入内容",
options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
],
},
},
{
label: "复选框",
component: "checkbox",
field: "checkbox",
props: {
type: "text",
placeholder: "请输入内容",
options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
],
},
},
{
label: "单选",
component: "radio",
field: "radio",
props: {
type: "text",
placeholder: "请输入内容",
options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
],
},
},
]);
let optionsTemp = JSON.parse(JSON.stringify(options.value));
/**
* @description 将表单数据与options融合
* @author yhx
* @param data 表单数据
* @return {Array} 融合后的数据
*/
const mergeOptions = (data: any) => {
// 深拷贝数据
const mergeData = JSON.parse(JSON.stringify(options.value));
// 将 data数据与options融合
mergeData.forEach((option: any) => {
if (option.field in data) {
option.value = data[option.field];
}
});
options.value = mergeData;
return mergeData;
};
/**
* @description 初始化表单状态
* @author yhx
* @param {Object} config 表单配置项
*
*/
const initForm = (state: string, data: any) => {
let resetForm = null as any;
switch (state) {
case "details": // 详情状态
mergeOptions(data);
break;
case "edit": // 编辑状态
mergeOptions(data);
resetForm = () => {
options.value = mergeOptions(data);
}; // 重置表单编辑状态
break;
case "create": // 创建状态
resetForm = () => (options.value = optionsTemp); // 重置表单编辑状态
break;
}
return { resetForm };
};
return { options, mergeOptions, initForm };
};
export default useForm;