Skip to content

Commit

Permalink
refactor: 重构表单
Browse files Browse the repository at this point in the history
  • Loading branch information
jsxiaosi committed Jan 18, 2022
1 parent d131d7b commit 676973d
Show file tree
Hide file tree
Showing 5 changed files with 557 additions and 108 deletions.
23 changes: 23 additions & 0 deletions src/components/Form/componentMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Component } from 'vue';
import { ElInput, ElDatePicker, ElCascader, ElSelect, ElCheckbox, ElRadio } from 'element-plus';

const componentMap = new Map<string, Component>();

componentMap.set('ElInput', ElInput);
componentMap.set('ElDatePicker', ElDatePicker);
componentMap.set('ElCascader', ElCascader);

componentMap.set('ElSelect', ElSelect);
componentMap.set('ElOption', ElSelect.Option);
componentMap.set('ElCheckbox', ElCheckbox);
componentMap.set('ElCheckboxGroup', ElCheckbox.CheckboxGroup);
componentMap.set('ElRadio', ElRadio);
componentMap.set('ElRadioGroup', ElRadio.RadioGroup);

const elComponentItem: Recordable = {
ElSelect: 'ElOption',
ElCheckboxGroup: 'ElCheckbox',
ElRadioGroup: 'ElRadio',
};

export { componentMap, elComponentItem };
72 changes: 14 additions & 58 deletions src/components/Form/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
:label-position="formOption.labelPosition"
label-width="120px"
>
<h1>{{ formOption }}</h1>
<!-- <div class="">标题</div> -->
<el-row v-for="(f, fix) in formOption.formIten" :key="fix" :gutter="f.gutter || 30">
<el-row v-for="(f, fix) in formOption.formItem" :key="fix" :gutter="f.gutter || 30">
<el-col
v-for="(fItem, fItemIx) in f.itemList"
:key="fItemIx"
Expand All @@ -19,59 +17,11 @@
:lg="f.lg || 8"
:xl="f.xl || 8"
>
<el-form-item :label="fItem.label" :prop="fItem.prop">
<slot
v-if="fItem.isSlot"
:form-item="fItem"
:form-mode="form"
:name="fItem.prop"
></slot>
<!-- 输入框 -->
<el-input
v-else-if="fItem.type === 'input'"
v-model="form[fItem.prop]"
:type="fItem.inputType"
></el-input>
<!-- 日期选择器 -->
<el-date-picker
v-else-if="fItem.type === 'dateTime'"
v-model="form[fItem.prop]"
type="datetimerange"
range-separator="To"
start-placeholder="Start date"
end-placeholder="End date"
>
</el-date-picker>
<!-- 单选框 -->
<el-radio-group v-else-if="fItem.type === 'radio'" v-model="form.resource">
<el-radio
v-for="(radio, radioIx) in fItem.options"
:key="radioIx"
:label="radio.label"
></el-radio>
</el-radio-group>
<!-- 下拉选择框 -->
<el-select
v-else-if="fItem.type === 'select'"
v-model="form[fItem.prop]"
:placeholder="fItem.placeholder"
>
<el-option
v-for="(select, selectIx) in fItem.options"
:key="selectIx"
:label="select.label"
:value="select.value"
></el-option>
</el-select>
<el-checkbox-group v-else-if="fItem.type === 'checkbox'" v-model="form[fItem.prop]">
<el-checkbox
v-for="(checkbox, checkboxIx) in fItem.options"
:key="checkboxIx"
:label="checkbox.label"
:name="form[fItem.prop]"
></el-checkbox>
</el-checkbox-group>
</el-form-item>
<FormItem :form-item="fItem" :form-model="form" :set-form-model="setFormModel">
<template v-for="item in Object.keys($slots)" #[item]="data">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</FormItem>
</el-col>
</el-row>
<el-form-item>
Expand All @@ -85,6 +35,7 @@
<script lang="ts" setup>
import { onMounted, reactive, PropType, ref } from 'vue';
import { FormProps, FormItemListProps } from './types/from';
import FormItem from './src/components/FormItem.vue';
defineProps({
formOption: {
Expand All @@ -100,19 +51,24 @@
const emit = defineEmits<{
(e: 'submitForm', form: FormItemListProps): void;
}>();
const form = reactive<any>({});
const form = reactive<any>({ name: '' });
const formRef = ref();
onMounted(() => {});
function setFormModel(key: string, value: any) {
form[key] = value;
}
const submitForm = () => {
formRef.value.validate((value: any) => {
console.log(value);
});
// console.log(formRef.value.validate());
console.log(form);
emit('submitForm', form);
};
const resetForm = () => {};
defineExpose({
Expand Down
80 changes: 80 additions & 0 deletions src/components/Form/src/components/FormItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script lang="tsx">
import { isFunction } from '@/utils/is';
import { defineComponent, PropType, Slots } from 'vue';
import { componentMap, elComponentItem } from '../../componentMap';
import { FormItemListProps } from '../../types/from';
export default defineComponent({
props: {
formItem: {
type: Object as PropType<FormItemListProps>,
default: () => {},
},
formModel: {
type: Object as PropType<Recordable>,
default: () => {},
},
},
setup(props, { slots }) {
const { formItem, formModel } = props as {
formItem: FormItemListProps;
formModel: {
[key: string]: string;
};
};
function renderComponent() {
const Comp = componentMap.get(formItem.component) as ReturnType<typeof defineComponent>;
return (
<Comp v-model={formModel[formItem.prop]} {...formItem.props}>
{childrenComponent()}
</Comp>
);
}
function childrenComponent() {
if (formItem.childrenComponent) {
const { options } = formItem.childrenComponent;
const compName = elComponentItem[formItem.component];
const Comp = componentMap.get(compName) as ReturnType<typeof defineComponent>;
return (
<>
{options?.map((res) => (
<Comp label={res.label} value={res.value} {...formItem.props}></Comp>
))}
</>
);
} else {
return null;
}
}
function getSlot(slots: Slots, slot = 'default', data?: any) {
if (!slots || !Reflect.has(slots, slot)) {
return null;
}
if (!isFunction(slots[slot])) {
console.error(`${slot} is not a function!`);
return null;
}
const slotFn = slots[slot];
if (!slotFn) return null;
return slotFn(data);
}
return () => {
const { formItem } = props;
const { prop, render, rules, label, isSlot } = formItem;
const values = { formModel: formModel, formItem: formItem };
const solfn = getSlot(slots, prop, values);
const getContent = () => {
return isSlot ? solfn : render ? render(values) : renderComponent();
};
return (
<el-form-item label={label} prop={prop} rules={rules}>
{getContent()}
</el-form-item>
);
};
},
});
</script>
27 changes: 18 additions & 9 deletions src/components/Form/types/from.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { VNode } from 'vue';

export interface FormProps {
labelPosition: string;
formIten: Array<FormItemProps>;
formItem: Array<FormItemProps>;
}

export interface FormItemProps {
Expand All @@ -14,22 +16,29 @@ export interface FormItemProps {
}

/**
* @param(type) input:输入框、dataTime:日期选择器、radio:单选框、select:下拉选择框
* @param(component) 组件名称,ElInput,ElSelect
* @param(label) 标签名
* @param(prop) form表单双向绑定字段
* @param(isSlot) 是否自定义内容
* @param(options) 选择项数据
* @param(inputType) input输入框类型 type为input生效
* @param(placeholder) 占位符
* @param(props) 组件属性
* @param(rules) 表单校验
* @param(childrenComponent) 子组件属性 类似ElSelect、ElCheckboxGroup、ElRadioGroup等组件
*/
export interface FormItemListProps {
type: string;
component: string;
label: string;
prop: string;
isSlot?: boolean;
options?: Array<FormSelectOptProps>;
inputType?: string;
placeholder?: string;
props?: object;
rules?: object | Array<object>;
childrenComponent?: {
props?: object;
options?: Array<FormSelectOptProps>;
};
render?: (data: {
formModel: Recordable;
formItem: FormItemListProps;
}) => VNode | VNode[] | string;
}

export interface FormSelectOptProps {
Expand Down
Loading

0 comments on commit 676973d

Please sign in to comment.