mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 14:31:41 +08:00
refactor(form): enhanced form customization and dynamic capabilities
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
<template v-for="schema in getSchema" :key="schema.field">
|
||||
<FormItem
|
||||
:tableAction="tableAction"
|
||||
:formActionType="formActionType"
|
||||
:schema="schema"
|
||||
:formProps="getProps"
|
||||
:allDefaultValues="defaultValueRef"
|
||||
@@ -164,7 +165,7 @@
|
||||
propsRef.value = mergeProps;
|
||||
}
|
||||
|
||||
const methods: Partial<FormActionType> = {
|
||||
const formActionType: Partial<FormActionType> = {
|
||||
getFieldsValue,
|
||||
setFieldsValue,
|
||||
resetFields,
|
||||
@@ -179,7 +180,7 @@
|
||||
|
||||
onMounted(() => {
|
||||
initDefault();
|
||||
emit('register', methods);
|
||||
emit('register', formActionType);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -191,7 +192,8 @@
|
||||
getProps,
|
||||
formElRef,
|
||||
getSchema,
|
||||
...methods,
|
||||
formActionType,
|
||||
...formActionType,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import type { PropType } from 'vue';
|
||||
import type { FormProps } from './types/form';
|
||||
import type { FormActionType, FormProps } from './types/form';
|
||||
import type { FormSchema } from './types/form';
|
||||
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
|
||||
import type { TableActionType } from '/@/components/Table';
|
||||
@@ -41,6 +41,9 @@ export default defineComponent({
|
||||
tableAction: {
|
||||
type: Object as PropType<TableActionType>,
|
||||
},
|
||||
formActionType: {
|
||||
type: Object as PropType<FormActionType>,
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const itemLabelWidthRef = useItemLabelWidth(toRef(props, 'schema'), toRef(props, 'formProps'));
|
||||
@@ -61,12 +64,12 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const getComponentsPropsRef = computed(() => {
|
||||
const { schema, tableAction, formModel } = props;
|
||||
const { schema, tableAction, formModel, formActionType } = props;
|
||||
const { componentProps = {} } = schema;
|
||||
if (!isFunction(componentProps)) {
|
||||
return componentProps;
|
||||
}
|
||||
return componentProps({ schema, tableAction, formModel }) || {};
|
||||
return componentProps({ schema, tableAction, formModel, formActionType }) || {};
|
||||
});
|
||||
|
||||
const getDisableRef = computed(() => {
|
||||
@@ -179,17 +182,27 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function renderComponent() {
|
||||
const { renderComponentContent, component, field, changeEvent = 'change' } = props.schema;
|
||||
const {
|
||||
renderComponentContent,
|
||||
component,
|
||||
field,
|
||||
changeEvent = 'change',
|
||||
valueField,
|
||||
} = props.schema;
|
||||
|
||||
const isCheck = component && ['Switch'].includes(component);
|
||||
const isCheck = component && ['Switch', 'Checkbox'].includes(component);
|
||||
|
||||
const eventKey = `on${upperFirst(changeEvent)}`;
|
||||
|
||||
const on = {
|
||||
[eventKey]: (e: any) => {
|
||||
if (propsData[eventKey]) {
|
||||
propsData[eventKey](e);
|
||||
}
|
||||
(props.formModel as any)[field] = e && e.target ? e.target.value : e;
|
||||
|
||||
const target = e ? e.target : null;
|
||||
const value = target ? (isCheck ? target.checked : target.value) : e;
|
||||
(props.formModel as any)[field] = value;
|
||||
},
|
||||
};
|
||||
const Comp = componentMap.get(component);
|
||||
@@ -215,17 +228,20 @@ export default defineComponent({
|
||||
propsData.codeField = field;
|
||||
propsData.formValues = unref(getValuesRef);
|
||||
const bindValue = {
|
||||
[isCheck ? 'checked' : 'value']: handleValue(component, field),
|
||||
[valueField || (isCheck ? 'checked' : 'value')]: handleValue(component, field),
|
||||
};
|
||||
if (!renderComponentContent) {
|
||||
return <Comp {...propsData} {...on} {...bindValue} />;
|
||||
}
|
||||
const compSlot = isFunction(renderComponentContent)
|
||||
? { ...renderComponentContent(unref(getValuesRef)) }
|
||||
: {
|
||||
default: () => renderComponentContent,
|
||||
};
|
||||
|
||||
return (
|
||||
<Comp {...propsData} {...on} {...bindValue}>
|
||||
{{
|
||||
...renderComponentContent(unref(getValuesRef)),
|
||||
}}
|
||||
{compSlot}
|
||||
</Comp>
|
||||
);
|
||||
}
|
||||
@@ -249,7 +265,7 @@ export default defineComponent({
|
||||
const { colon } = props.formProps;
|
||||
const getContent = () => {
|
||||
return slot
|
||||
? getSlot(slots, slot)
|
||||
? getSlot(slots, slot, unref(getValuesRef))
|
||||
: render
|
||||
? render(unref(getValuesRef))
|
||||
: renderComponent();
|
||||
@@ -276,7 +292,7 @@ export default defineComponent({
|
||||
const { isIfShow, isShow } = getShow();
|
||||
const getContent = () => {
|
||||
return colSlot
|
||||
? getSlot(slots, colSlot)
|
||||
? getSlot(slots, colSlot, unref(getValuesRef))
|
||||
: renderColContent
|
||||
? renderColContent(unref(getValuesRef))
|
||||
: renderItem();
|
||||
|
@@ -14,8 +14,8 @@ import {
|
||||
Switch,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Transfer,
|
||||
} from 'ant-design-vue';
|
||||
import RadioButtonGroup from './components/RadioButtonGroup.vue';
|
||||
|
||||
import { ComponentType } from './types/index';
|
||||
|
||||
@@ -30,13 +30,13 @@ componentMap.set('InputNumber', InputNumber);
|
||||
componentMap.set('AutoComplete', AutoComplete);
|
||||
|
||||
componentMap.set('Select', Select);
|
||||
componentMap.set('SelectOptGroup', Select.OptGroup);
|
||||
componentMap.set('SelectOption', Select.Option);
|
||||
// componentMap.set('SelectOptGroup', Select.OptGroup);
|
||||
// componentMap.set('SelectOption', Select.Option);
|
||||
componentMap.set('TreeSelect', TreeSelect);
|
||||
componentMap.set('Transfer', Transfer);
|
||||
componentMap.set('Radio', Radio);
|
||||
// componentMap.set('Transfer', Transfer);
|
||||
// componentMap.set('Radio', Radio);
|
||||
componentMap.set('Switch', Switch);
|
||||
componentMap.set('RadioButton', Radio.Button);
|
||||
componentMap.set('RadioButtonGroup', RadioButtonGroup);
|
||||
componentMap.set('RadioGroup', Radio.Group);
|
||||
componentMap.set('Checkbox', Checkbox);
|
||||
componentMap.set('CheckboxGroup', Checkbox.Group);
|
||||
|
61
src/components/Form/src/components/RadioButtonGroup.vue
Normal file
61
src/components/Form/src/components/RadioButtonGroup.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<RadioGroup v-bind="$attrs" v-model:value="valueRef" button-style="solid">
|
||||
<template v-for="item in getOptions" :key="`${item.value}`">
|
||||
<RadioButton :value="item.value"> {{ item.label }} </RadioButton>
|
||||
</template>
|
||||
</RadioGroup>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, PropType, watch, unref, computed } from 'vue';
|
||||
import { Radio } from 'ant-design-vue';
|
||||
import {} from 'ant-design-vue/es/radio/Group';
|
||||
import { isString } from '/@/utils/is';
|
||||
|
||||
type OptionsItem = { label: string; value: string; disabled?: boolean };
|
||||
type RadioItem = string | OptionsItem;
|
||||
export default defineComponent({
|
||||
name: 'RadioButtonGroup',
|
||||
components: {
|
||||
RadioGroup: Radio.Group,
|
||||
RadioButton: Radio.Button,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<RadioItem[]>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const valueRef = ref('');
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(v = '') => {
|
||||
valueRef.value = v;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => unref(valueRef),
|
||||
() => {
|
||||
emit('change', valueRef.value);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const getOptions = computed((): OptionsItem[] => {
|
||||
const { options } = props;
|
||||
if (!options || options.length === 0) return [];
|
||||
const isStringArr = options.some((item) => isString(item));
|
||||
if (!isStringArr) return options as OptionsItem[];
|
||||
return options.map((item) => ({ label: item, value: item })) as OptionsItem[];
|
||||
});
|
||||
|
||||
return { valueRef, getOptions };
|
||||
},
|
||||
});
|
||||
</script>
|
@@ -7,6 +7,10 @@ import { TableActionType } from '../../../Table/src/types/table';
|
||||
|
||||
export type FieldMapToTime = [string, [string, string], string?][];
|
||||
|
||||
export type Rule = RuleObject & {
|
||||
trigger?: 'blur' | 'change' | ['change', 'blur'];
|
||||
};
|
||||
|
||||
export interface RenderCallbackParams {
|
||||
schema: FormSchema;
|
||||
values: any;
|
||||
@@ -98,7 +102,10 @@ export interface FormProps {
|
||||
export interface FormSchema {
|
||||
// 字段名
|
||||
field: string;
|
||||
// 内部值更改触发的事件名,默认 change
|
||||
changeEvent?: string;
|
||||
// v-model绑定的变量名 默认 value
|
||||
valueField?: string;
|
||||
// 标签名
|
||||
label: string;
|
||||
// 文本右侧帮助文本
|
||||
@@ -113,13 +120,18 @@ export interface FormSchema {
|
||||
component: ComponentType;
|
||||
// 组件参数
|
||||
componentProps?:
|
||||
| ((opt: { schema: FormSchema; tableAction: TableActionType; formModel: any }) => any)
|
||||
| ((opt: {
|
||||
schema: FormSchema;
|
||||
tableAction: TableActionType;
|
||||
formActionType: FormActionType;
|
||||
formModel: any;
|
||||
}) => any)
|
||||
| object;
|
||||
// 必填
|
||||
required?: boolean;
|
||||
|
||||
// 校验规则
|
||||
rules?: RuleObject[];
|
||||
rules?: Rule[];
|
||||
// 校验信息是否加入label
|
||||
rulesMessageJoinLabel?: boolean;
|
||||
|
||||
@@ -146,7 +158,11 @@ export interface FormSchema {
|
||||
// 渲染 col内容,需要外层包裹 form-item
|
||||
renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
|
||||
|
||||
renderComponentContent?: (renderCallbackParams: RenderCallbackParams) => any;
|
||||
renderComponentContent?:
|
||||
| ((renderCallbackParams: RenderCallbackParams) => any)
|
||||
| VNode
|
||||
| VNode[]
|
||||
| string;
|
||||
|
||||
// 自定义slot, 在 from-item内
|
||||
slot?: string;
|
||||
@@ -156,7 +172,7 @@ export interface FormSchema {
|
||||
|
||||
dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
|
||||
|
||||
dynamicRules?: (renderCallbackParams: RenderCallbackParams) => RuleObject[];
|
||||
dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
|
||||
}
|
||||
export interface HelpComponentProps {
|
||||
maxWidth: string;
|
||||
|
@@ -93,8 +93,8 @@ export type ComponentType =
|
||||
| 'SelectOption'
|
||||
| 'TreeSelect'
|
||||
| 'Transfer'
|
||||
| 'Radio'
|
||||
| 'RadioButton'
|
||||
// | 'Radio'
|
||||
| 'RadioButtonGroup'
|
||||
| 'RadioGroup'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
|
@@ -87,7 +87,7 @@ export interface GetColumnsParams {
|
||||
export type SizeType = 'default' | 'middle' | 'small' | 'large';
|
||||
|
||||
export interface TableActionType {
|
||||
reload: (opt?: FetchParams) => Promise<void>;
|
||||
// reload: (opt?: FetchParams) => Promise<void>;
|
||||
getSelectRows: () => any[];
|
||||
clearSelectedRowKeys: () => void;
|
||||
getSelectRowKeys: () => string[];
|
||||
|
Reference in New Issue
Block a user