From 1e9348276d56ee29ea8cfde13fb7e62932e3712c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=AB=E6=9D=B0?= <> Date: Mon, 13 Jan 2025 12:47:04 +0800 Subject: [PATCH 1/4] feat: Added judgment to determine whether to trigger the countdown --- .../ui-kit/shadcn-ui/src/components/pin-input/input.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue b/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue index 941198d158e..87d2ef562ea 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue @@ -14,7 +14,9 @@ const { codeLength = 6, createText = async () => {}, disabled = false, - handleSendCode = async () => {}, + handleSendCode = async () => { + return true; + }, loading = false, maxTime = 60, } = defineProps(); @@ -59,7 +61,8 @@ function handleComplete(e: string[]) { async function handleSend(e: Event) { try { e?.preventDefault(); - await handleSendCode(); + const bool = await handleSendCode(); + if (!bool) return; countdown.value = maxTime; startCountdown(); } catch (error) { From 44f911b8e070207e1a1cc8494420cc1da6b2c429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=AB=E6=9D=B0?= <> Date: Mon, 13 Jan 2025 13:17:15 +0800 Subject: [PATCH 2/4] feat: Added mandatory check presets for multiple selections --- apps/web-antd/src/adapter/form.ts | 6 ++++++ apps/web-naive/src/adapter/form.ts | 6 ++++++ docs/src/_env/adapter/form.ts | 6 ++++++ docs/src/components/common-ui/vben-form.md | 6 ++++++ .../@core/ui-kit/form-ui/src/form-render/form-field.vue | 4 +++- packages/@core/ui-kit/form-ui/src/types.ts | 5 +++++ playground/src/adapter/form.ts | 6 ++++++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/web-antd/src/adapter/form.ts b/apps/web-antd/src/adapter/form.ts index 65ff793b6ec..cec36bdbf4a 100644 --- a/apps/web-antd/src/adapter/form.ts +++ b/apps/web-antd/src/adapter/form.ts @@ -36,6 +36,12 @@ setupVbenForm({ } return true; }, + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, }, }); diff --git a/apps/web-naive/src/adapter/form.ts b/apps/web-naive/src/adapter/form.ts index 2f2ed2abe39..de18b1ccf19 100644 --- a/apps/web-naive/src/adapter/form.ts +++ b/apps/web-naive/src/adapter/form.ts @@ -32,6 +32,12 @@ setupVbenForm({ } return true; }, + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, }, }); diff --git a/docs/src/_env/adapter/form.ts b/docs/src/_env/adapter/form.ts index d8b51c254d1..2875c3f049d 100644 --- a/docs/src/_env/adapter/form.ts +++ b/docs/src/_env/adapter/form.ts @@ -24,6 +24,12 @@ setupVbenForm({ }, }, defineRules: { + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, required: (value, _params, ctx) => { if (value === undefined || value === null || value.length === 0) { return $t('ui.formRules.required', [ctx.label]); diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index a15c366f01e..112bb844965 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -48,6 +48,12 @@ setupVbenForm({ }, }, defineRules: { + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, // 输入项目必填国际化适配 required: (value, _params, ctx) => { if (value === undefined || value === null || value.length === 0) { 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 7fa942e45a6..5233019b6b7 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 @@ -109,7 +109,9 @@ const shouldRequired = computed(() => { } if (isString(currentRules.value)) { - return ['required', 'selectRequired'].includes(currentRules.value); + return ['multipleRequired', 'required', 'selectRequired'].includes( + currentRules.value, + ); } let isOptional = currentRules?.value?.isOptional?.(); diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 07a2afd43ec..57b2a68b817 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -395,6 +395,11 @@ export interface VbenFormAdapterOptions< modelPropNameMap?: Partial>; }; defineRules?: { + multipleRequired?: ( + value: any, + params: any, + ctx: Record, + ) => boolean | string; required?: ( value: any, params: any, diff --git a/playground/src/adapter/form.ts b/playground/src/adapter/form.ts index dfe8fed0d31..1be1bdcdb28 100644 --- a/playground/src/adapter/form.ts +++ b/playground/src/adapter/form.ts @@ -21,6 +21,12 @@ setupVbenForm({ }, }, defineRules: { + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, // 输入项目必填国际化适配 required: (value, _params, ctx) => { if (value === undefined || value === null || value.length === 0) { From 3609123facffc0db1734c35d565246dab455b68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=AB=E6=9D=B0?= <> Date: Mon, 13 Jan 2025 13:18:40 +0800 Subject: [PATCH 3/4] feat: Added mandatory check presets for multiple selections --- apps/web-ele/src/adapter/form.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/web-ele/src/adapter/form.ts b/apps/web-ele/src/adapter/form.ts index 13ae9c428a3..22e94bce2fc 100644 --- a/apps/web-ele/src/adapter/form.ts +++ b/apps/web-ele/src/adapter/form.ts @@ -28,6 +28,12 @@ setupVbenForm({ } return true; }, + multipleRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, }, }); From b7db3773987a26f2f12dc3b3393876b408c24f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=AB=E6=9D=B0?= <> Date: Mon, 13 Jan 2025 14:01:22 +0800 Subject: [PATCH 4/4] feat: Add a trigger hook function for each item of the form and a global hook function --- docs/src/components/common-ui/vben-form.md | 4 + .../form-ui/src/form-render/form-field.vue | 79 +++++++++++++++++-- .../ui-kit/form-ui/src/form-render/form.vue | 2 + packages/@core/ui-kit/form-ui/src/types.ts | 4 + 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 112bb844965..1c994192d92 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -365,6 +365,10 @@ export interface ActionButtonOptions { ```ts export interface FormCommonConfig { + /** + * 是否自动赋值 + */ + autoDefaultValue?: boolean; /** * 所有表单项的props */ 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 5233019b6b7..d17b358ffb5 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, useTemplateRef, watch } from 'vue'; +import { computed, nextTick, onMounted, useTemplateRef, watch } from 'vue'; import { FormControl, @@ -26,10 +26,12 @@ import { isEventObjectLike } from './helper'; interface Props extends FormSchema {} const { + autoDefaultValue = false, colon, commonComponentProps, component, componentProps, + defaultValue, dependencies, description, disabled, @@ -163,6 +165,16 @@ const computedProps = computed(() => { ...dynamicComponentProps.value, }; }); +const computedItemProps = computed(() => { + const finalComponentProps = isFunction(componentProps) + ? componentProps(values.value, formApi!) + : componentProps; + + return { + ...finalComponentProps, + ...dynamicComponentProps.value, + }; +}); watch( () => computedProps.value?.autofocus, @@ -201,6 +213,23 @@ const fieldProps = computed(() => { }; }); +onMounted(() => { + if ( + autoDefaultValue && + Reflect.has(commonComponentProps, 'change') && + defaultValue + ) { + const value = (formApi && formApi.values[fieldName]) ?? defaultValue; + commonComponentProps.change(value, { + e: value, + ...computedProps.value, + emptyStateValue, + formApi, + name: fieldName, + }); + } +}); + function fieldBindEvent(slotProps: Record) { const modelValue = slotProps.componentField.modelValue; const handler = slotProps.componentField['onUpdate:modelValue']; @@ -210,6 +239,19 @@ function fieldBindEvent(slotProps: Record) { (isString(component) ? componentBindEventMap.value?.[component] : null); let value = modelValue; + + const change = (...e: any) => { + handler(...e); + if (Reflect.has(commonComponentProps, 'change')) { + commonComponentProps.change(e[0], { + e, + ...slotProps.componentField, + ...computedProps.value, + [bindEventField || '']: value === undefined ? emptyStateValue : value, + formApi, + }); + } + }; // antd design 的一些组件会传递一个 event 对象 if (modelValue && isObject(modelValue) && bindEventField) { value = isEventObjectLike(modelValue) @@ -219,7 +261,7 @@ function fieldBindEvent(slotProps: Record) { if (bindEventField) { return { - [`onUpdate:${bindEventField}`]: handler, + [`onUpdate:${bindEventField}`]: change, [bindEventField]: value === undefined ? emptyStateValue : value, onChange: disabledOnChangeListener ? undefined @@ -243,17 +285,42 @@ function fieldBindEvent(slotProps: Record) { function createComponentProps(slotProps: Record) { const bindEvents = fieldBindEvent(slotProps); - - const binds = { + const assginData = { ...slotProps.componentField, ...computedProps.value, ...bindEvents, + }; + + const change = (key: string, value: any, ...e: any) => { + if (!Reflect.has(assginData, key)) return; + (assginData as any)[key](...e); + + nextTick(() => { + if (Reflect.has(computedItemProps.value, 'change')) { + computedItemProps.value.change(value, { + e, + ...slotProps.componentField, + ...computedProps.value, + ...bindEvents, + formApi, + }); + } + }); + }; + const binds = { + ...assginData, ...(Reflect.has(computedProps.value, 'onChange') - ? { onChange: computedProps.value.onChange } + ? { onChange: (...e: any) => change('onChange', e[0], ...e) } : {}), ...(Reflect.has(computedProps.value, 'onInput') - ? { onInput: computedProps.value.onInput } + ? { onInput: (...e: any) => change('onInput', e[0], ...e) } : {}), + 'onUpdate:modelValue': (...e: any) => { + change('onUpdate:modelValue', e[0], ...e); + }, + 'onUpdate:value': (...e: any) => { + change('onUpdate:value', e[0], ...e); + }, }; return binds; diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form.vue b/packages/@core/ui-kit/form-ui/src/form-render/form.vue index ff827c42a09..9ecb2cd4dbe 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form.vue +++ b/packages/@core/ui-kit/form-ui/src/form-render/form.vue @@ -85,6 +85,7 @@ const computedSchema = computed( formFieldProps: Record; })[] => { const { + autoDefaultValue = false, colon = false, componentProps = {}, controlClass = '', @@ -111,6 +112,7 @@ const computedSchema = computed( : false; return { + autoDefaultValue, colon, disabled, disabledOnChangeListener, diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 57b2a68b817..47a4e8a50cc 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -137,6 +137,10 @@ type ComponentProps = | MaybeComponentProps; export interface FormCommonConfig { + /** + * 是否自动赋值 + */ + autoDefaultValue?: boolean; /** * 在Label后显示一个冒号 */