From 0cd865e211ad9756c2460389ce43d3a49ba9ee6c Mon Sep 17 00:00:00 2001 From: Vben Date: Thu, 3 Oct 2024 14:22:18 +0800 Subject: [PATCH] fix: fixed an error in the form onChange within Naive (#4558) * fix: fixed an error in the form onChange within Naive * chore: update --- apps/web-antd/src/adapter/form.ts | 5 +- apps/web-ele/src/adapter/form.ts | 19 ++++++-- apps/web-naive/src/adapter/form.ts | 20 ++++++-- .../src/router/routes/modules/demos.ts | 1 - packages/@core/ui-kit/form-ui/src/config.ts | 11 ++++- .../form-ui/src/form-render/form-field.vue | 46 ++++++++++--------- .../ui-kit/form-ui/src/form-render/form.vue | 29 ++++++++---- packages/@core/ui-kit/form-ui/src/types.ts | 6 +++ .../@core/ui-kit/form-ui/src/vben-form.vue | 7 ++- .../ui-kit/form-ui/src/vben-use-form.vue | 8 +++- playground/src/adapter/form.ts | 5 +- pnpm-lock.yaml | 36 +++++++-------- 12 files changed, 128 insertions(+), 65 deletions(-) diff --git a/apps/web-antd/src/adapter/form.ts b/apps/web-antd/src/adapter/form.ts index b52a8840be3..48c981f75aa 100644 --- a/apps/web-antd/src/adapter/form.ts +++ b/apps/web-antd/src/adapter/form.ts @@ -4,7 +4,8 @@ import type { VbenFormProps, } from '@vben/common-ui'; -import { type Component, h, type SetupContext } from 'vue'; +import type { Component, SetupContext } from 'vue'; +import { h } from 'vue'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; @@ -63,7 +64,7 @@ const withDefaultPlaceholder = ( ) => { return (props: any, { attrs, slots }: Omit) => { const placeholder = props?.placeholder || $t(`placeholder.${type}`); - return h(component, { ...props, attrs, placeholder }, slots); + return h(component, { ...props, ...attrs, placeholder }, slots); }; }; diff --git a/apps/web-ele/src/adapter/form.ts b/apps/web-ele/src/adapter/form.ts index b4fa0a06b90..36e200336a0 100644 --- a/apps/web-ele/src/adapter/form.ts +++ b/apps/web-ele/src/adapter/form.ts @@ -4,6 +4,7 @@ import type { VbenFormProps, } from '@vben/common-ui'; +import type { Component, SetupContext } from 'vue'; import { h } from 'vue'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; @@ -42,6 +43,16 @@ export type FormComponentType = | 'Upload' | BaseFormComponentType; +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`placeholder.${type}`); + return h(component, { ...props, ...attrs, placeholder }, slots); + }; +}; + // 初始化表单组件,并注册到form组件内部 setupVbenForm({ components: { @@ -56,14 +67,14 @@ setupVbenForm({ return h(ElButton, { ...props, attrs, type: 'primary' }, slots); }, Divider: ElDivider, - Input: ElInput, - InputNumber: ElInputNumber, + Input: withDefaultPlaceholder(ElInput, 'input'), + InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'), RadioGroup: ElRadioGroup, - Select: ElSelect, + Select: withDefaultPlaceholder(ElSelect, 'select'), Space: ElSpace, Switch: ElSwitch, TimePicker: ElTimePicker, - TreeSelect: ElTreeSelect, + TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'), Upload: ElUpload, }, config: { diff --git a/apps/web-naive/src/adapter/form.ts b/apps/web-naive/src/adapter/form.ts index 552919af608..1c051b4a8e2 100644 --- a/apps/web-naive/src/adapter/form.ts +++ b/apps/web-naive/src/adapter/form.ts @@ -4,6 +4,7 @@ import type { VbenFormProps, } from '@vben/common-ui'; +import type { Component, SetupContext } from 'vue'; import { h } from 'vue'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; @@ -43,6 +44,16 @@ export type FormComponentType = | 'Upload' | BaseFormComponentType; +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', +) => { + return (props: any, { attrs, slots }: Omit) => { + const placeholder = props?.placeholder || $t(`placeholder.${type}`); + return h(component, { ...props, ...attrs, placeholder }, slots); + }; +}; + // 初始化表单组件,并注册到form组件内部 setupVbenForm({ components: { @@ -62,17 +73,18 @@ setupVbenForm({ ); }, Divider: NDivider, - Input: NInput, - InputNumber: NInputNumber, + Input: withDefaultPlaceholder(NInput, 'input'), + InputNumber: withDefaultPlaceholder(NInputNumber, 'input'), RadioGroup: NRadioGroup, - Select: NSelect, + Select: withDefaultPlaceholder(NSelect, 'select'), Space: NSpace, Switch: NSwitch, TimePicker: NTimePicker, - TreeSelect: NTreeSelect, + TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'), Upload: NUpload, }, config: { + disabledOnChangeListener: true, baseModelPropName: 'value', modelPropNameMap: { Checkbox: 'checked', diff --git a/apps/web-naive/src/router/routes/modules/demos.ts b/apps/web-naive/src/router/routes/modules/demos.ts index 5a2dc12a036..cac10a9bf98 100644 --- a/apps/web-naive/src/router/routes/modules/demos.ts +++ b/apps/web-naive/src/router/routes/modules/demos.ts @@ -25,7 +25,6 @@ const routes: RouteRecordRaw[] = [ }, { meta: { - icon: 'mdi:shield-key-outline', title: $t('page.demos.table'), }, name: 'Table', diff --git a/packages/@core/ui-kit/form-ui/src/config.ts b/packages/@core/ui-kit/form-ui/src/config.ts index 0ab8ce3436b..7c118634673 100644 --- a/packages/@core/ui-kit/form-ui/src/config.ts +++ b/packages/@core/ui-kit/form-ui/src/config.ts @@ -1,4 +1,8 @@ -import type { BaseFormComponentType, VbenFormAdapterOptions } from './types'; +import type { + BaseFormComponentType, + FormCommonConfig, + VbenFormAdapterOptions, +} from './types'; import type { Component } from 'vue'; import { h } from 'vue'; @@ -16,6 +20,8 @@ import { defineRule } from 'vee-validate'; const DEFAULT_MODEL_PROP_NAME = 'modelValue'; +export const DEFAULT_FORM_COMMON_CONFIG: FormCommonConfig = {}; + export const COMPONENT_MAP: Record = { DefaultResetActionButton: h(VbenButton, { size: 'sm', variant: 'outline' }), DefaultSubmitActionButton: h(VbenButton, { size: 'sm', variant: 'default' }), @@ -37,6 +43,9 @@ export function setupVbenForm< >(options: VbenFormAdapterOptions) { const { components, config, defineRules } = options; + DEFAULT_FORM_COMMON_CONFIG.disabledOnChangeListener = + config?.disabledOnChangeListener ?? false; + if (defineRules) { for (const key of Object.keys(defineRules)) { defineRule(key, defineRules[key as never]); diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue index 28ed6124345..53ceff61c24 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue +++ b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue @@ -3,7 +3,7 @@ import type { ZodType } from 'zod'; import type { FormSchema, MaybeComponentProps } from '../types'; -import { computed, nextTick, ref, watch } from 'vue'; +import { computed, nextTick, useTemplateRef, watch } from 'vue'; import { FormControl, @@ -32,6 +32,7 @@ const { dependencies, description, disabled, + disabledOnChangeListener, fieldName, formFieldProps, label, @@ -49,19 +50,10 @@ const { componentBindEventMap, componentMap, isVertical } = useFormContext(); const formRenderProps = injectRenderFormProps(); const values = useFormValues(); const errors = useFieldError(fieldName); +const fieldComponentRef = useTemplateRef('fieldComponentRef'); const formApi = formRenderProps.form; const isInValid = computed(() => errors.value?.length > 0); -const fieldComponentRef = ref(null); -const focus = () => { - if ( - fieldComponentRef.value && - typeof fieldComponentRef.value.focus === 'function' && - document.activeElement !== fieldComponentRef.value // 检查当前是否有元素被聚焦 - ) { - fieldComponentRef.value.focus(); - } -}; const fieldComponent = computed(() => { const finalComponent = isString(component) @@ -171,7 +163,7 @@ watch( (value) => { if (value === true) { nextTick(() => { - focus(); + autofocus(); }); } }, @@ -222,15 +214,16 @@ function fieldBindEvent(slotProps: Record) { return { [`onUpdate:${bindEventField}`]: handler, [bindEventField]: value, - onChange: (e: Record) => { - const shouldUnwrap = isEventObjectLike(e); - const onChange = slotProps?.componentField?.onChange; - if (!shouldUnwrap) { - return onChange?.(e); - } - - return onChange?.(e?.target?.[bindEventField] ?? e); - }, + onChange: disabledOnChangeListener + ? undefined + : (e: Record) => { + const shouldUnwrap = isEventObjectLike(e); + const onChange = slotProps?.componentField?.onChange; + if (!shouldUnwrap) { + return onChange?.(e); + } + return onChange?.(e?.target?.[bindEventField] ?? e); + }, onInput: () => {}, }; } @@ -248,6 +241,17 @@ function createComponentProps(slotProps: Record) { return binds; } + +function autofocus() { + if ( + fieldComponentRef.value && + isFunction(fieldComponentRef.value.focus) && + // 检查当前是否有元素被聚焦 + document.activeElement !== fieldComponentRef.value + ) { + fieldComponentRef.value?.focus?.(); + } +}