fix(BasicForm): useForm 中 scheme 选项 slot 与 component冲突 (#3133)

* fix(BasicForm): useForm 中 scheme 选项 slot 与 component冲突

* chore: add type-predicate utils

* chore: add FormSchemaInner type
This commit is contained in:
Li Kui 2023-10-16 16:56:26 +08:00 committed by GitHub
parent d30fd1d546
commit 0bb76a86d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 64 additions and 23 deletions

View File

@ -38,7 +38,7 @@
</Form> </Form>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form'; import type { FormActionType, FormProps, FormSchemaInner as FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks'; import type { AdvanceState } from './types/hooks';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
@ -125,7 +125,12 @@
isHandleDateDefaultValue = true, isHandleDateDefaultValue = true,
} = schema; } = schema;
// handle date type // handle date type
if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) { if (
isHandleDateDefaultValue &&
defaultValue &&
component &&
dateItemType.includes(component)
) {
const valueFormat = componentProps ? componentProps['valueFormat'] : null; const valueFormat = componentProps ? componentProps['valueFormat'] : null;
if (!Array.isArray(defaultValue)) { if (!Array.isArray(defaultValue)) {
schema.defaultValue = valueFormat schema.defaultValue = valueFormat

View File

@ -2,7 +2,12 @@
import { type Recordable, type Nullable } from '@vben/types'; import { type Recordable, type Nullable } from '@vben/types';
import type { PropType, Ref } from 'vue'; import type { PropType, Ref } from 'vue';
import { computed, defineComponent, toRefs, unref } from 'vue'; import { computed, defineComponent, toRefs, unref } from 'vue';
import type { FormActionType, FormProps, FormSchema } from '../types/form'; import {
isComponentFormSchema,
type FormActionType,
type FormProps,
type FormSchemaInner as FormSchema,
} from '../types/form';
import type { Rule as ValidationRule } from 'ant-design-vue/lib/form/interface'; import type { Rule as ValidationRule } from 'ant-design-vue/lib/form/interface';
import type { TableActionType } from '/@/components/Table'; import type { TableActionType } from '/@/components/Table';
import { Col, Divider, Form } from 'ant-design-vue'; import { Col, Divider, Form } from 'ant-design-vue';
@ -241,6 +246,9 @@
} }
function renderComponent() { function renderComponent() {
if (!isComponentFormSchema(props.schema)) {
return null;
}
const { const {
renderComponentContent, renderComponentContent,
component, component,
@ -352,7 +360,7 @@
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
// TODO autoLink=false // TODO autoLink=false
if (NO_AUTO_LINK_COMPONENTS.includes(component)) { if (component && NO_AUTO_LINK_COMPONENTS.includes(component)) {
props.schema && props.schema &&
(props.schema.itemProps! = { (props.schema.itemProps! = {
autoLink: false, autoLink: false,
@ -382,7 +390,7 @@
return () => { return () => {
const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema; const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema;
if (!componentMap.has(component) && !slot) { if (!component || (!componentMap.has(component) && !slot)) {
return null; return null;
} }

View File

@ -1,7 +1,7 @@
import type { ColEx } from '../types'; import type { ColEx } from '../types';
import type { AdvanceState } from '../types/hooks'; import type { AdvanceState } from '../types/hooks';
import { ComputedRef, getCurrentInstance, Ref, shallowReactive, computed, unref, watch } from 'vue'; import { ComputedRef, getCurrentInstance, Ref, shallowReactive, computed, unref, watch } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';

View File

@ -1,5 +1,9 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import type { FormSchema, FormActionType, FormProps } from '../types/form'; import {
type FormSchemaInner as FormSchema,
type FormActionType,
type FormProps,
} from '../types/form';
import { unref, nextTick, watchEffect } from 'vue'; import { unref, nextTick, watchEffect } from 'vue';
@ -29,7 +33,7 @@ export async function useAutoFocus({
const firstItem = schemas[0]; const firstItem = schemas[0];
// Only open when the first form item is input type // Only open when the first form item is input type
if (!firstItem.component.includes('Input')) { if (!firstItem.component || !firstItem.component.includes('Input')) {
return; return;
} }

View File

@ -1,4 +1,9 @@
import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; import type {
FormProps,
FormActionType,
UseFormReturnType,
FormSchemaInner as FormSchema,
} from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface'; import type { NamePath } from 'ant-design-vue/lib/form/interface';
import type { DynamicProps } from '/#/utils'; import type { DynamicProps } from '/#/utils';
import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; import { ref, onUnmounted, unref, nextTick, watch } from 'vue';

View File

@ -1,5 +1,5 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import type { FormProps, FormSchema, FormActionType } from '../types/form'; import type { FormProps, FormSchemaInner as FormSchema, FormActionType } from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface'; import type { NamePath } from 'ant-design-vue/lib/form/interface';
import { unref, toRaw, nextTick } from 'vue'; import { unref, toRaw, nextTick } from 'vue';
import { import {
@ -335,7 +335,7 @@ export function useFormEvents({
*/ */
function itemIsDateType(key: string) { function itemIsDateType(key: string) {
return unref(getSchema).some((item) => { return unref(getSchema).some((item) => {
return item.field === key ? dateItemType.includes(item.component) : false; return item.field === key && item.component ? dateItemType.includes(item.component) : false;
}); });
} }

View File

@ -2,7 +2,7 @@ import { isArray, isFunction, isNotEmpty, isObject, isString, isNullOrUnDef } fr
import { dateUtil } from '/@/utils/dateUtil'; import { dateUtil } from '/@/utils/dateUtil';
import { unref } from 'vue'; import { unref } from 'vue';
import type { Ref, ComputedRef } from 'vue'; import type { Ref, ComputedRef } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
import { cloneDeep, get, set, unset } from 'lodash-es'; import { cloneDeep, get, set, unset } from 'lodash-es';
interface UseFormValuesContext { interface UseFormValuesContext {

View File

@ -1,6 +1,6 @@
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import type { FormProps, FormSchema } from '../types/form'; import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
import { isNumber } from '/@/utils/is'; import { isNumber } from '/@/utils/is';
export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) { export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) {

View File

@ -13,7 +13,7 @@ export type Rule = RuleObject & {
}; };
export interface RenderCallbackParams { export interface RenderCallbackParams {
schema: FormSchema; schema: FormSchemaInner;
values: Recordable; values: Recordable;
model: Recordable; model: Recordable;
field: string; field: string;
@ -29,12 +29,12 @@ export interface FormActionType {
resetFields: () => Promise<void>; resetFields: () => Promise<void>;
getFieldsValue: () => Recordable; getFieldsValue: () => Recordable;
clearValidate: (name?: string | string[]) => Promise<void>; clearValidate: (name?: string | string[]) => Promise<void>;
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>; updateSchema: (data: Partial<FormSchemaInner> | Partial<FormSchemaInner>[]) => Promise<void>;
resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>; resetSchema: (data: Partial<FormSchemaInner> | Partial<FormSchemaInner>[]) => Promise<void>;
setProps: (formProps: Partial<FormProps>) => Promise<void>; setProps: (formProps: Partial<FormProps>) => Promise<void>;
removeSchemaByField: (field: string | string[]) => Promise<void>; removeSchemaByField: (field: string | string[]) => Promise<void>;
appendSchemaByField: ( appendSchemaByField: (
schema: FormSchema | FormSchema[], schema: FormSchemaInner | FormSchemaInner[],
prefixField: string | undefined, prefixField: string | undefined,
first?: boolean | undefined, first?: boolean | undefined,
) => Promise<void>; ) => Promise<void>;
@ -127,7 +127,8 @@ export type RenderOpts = {
disabled: boolean; disabled: boolean;
[key: string]: any; [key: string]: any;
}; };
export interface FormSchema {
interface BaseFormSchema {
// Field name // Field name
field: string; field: string;
// Extra Fields name[] // Extra Fields name[]
@ -151,8 +152,6 @@ export interface FormSchema {
labelWidth?: string | number; labelWidth?: string | number;
// Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself // Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself
disabledLabelWidth?: boolean; disabledLabelWidth?: boolean;
// render component
component: ComponentType;
// Component parameters // Component parameters
componentProps?: componentProps?:
| ((opt: { | ((opt: {
@ -214,9 +213,6 @@ export interface FormSchema {
| VNode[] | VNode[]
| string; | string;
// Custom slot, in from-item
slot?: string;
// Custom slot, similar to renderColContent // Custom slot, similar to renderColContent
colSlot?: string; colSlot?: string;
@ -224,6 +220,29 @@ export interface FormSchema {
dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[]; dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
} }
export interface ComponentFormSchema extends BaseFormSchema {
// render component
component: ComponentType;
}
export interface SlotFormSchema extends BaseFormSchema {
// Custom slot, in from-item
slot: string;
}
export type FormSchema = ComponentFormSchema | SlotFormSchema;
export type FormSchemaInner = Partial<ComponentFormSchema> &
Partial<SlotFormSchema> &
BaseFormSchema;
export function isSlotFormSchema(schema: FormSchemaInner): schema is SlotFormSchema {
return 'slot' in schema;
}
export function isComponentFormSchema(schema: FormSchemaInner): schema is ComponentFormSchema {
return !isSlotFormSchema(schema);
}
export interface HelpComponentProps { export interface HelpComponentProps {
maxWidth: string; maxWidth: string;
// Whether to display the serial number // Whether to display the serial number