This commit is contained in:
dap
2024-12-17 08:01:02 +08:00
101 changed files with 853 additions and 193 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@vben-core/form-ui",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -46,11 +46,15 @@ export function setupVbenForm<
>(options: VbenFormAdapterOptions<T>) {
const { config, defineRules } = options;
const { disabledOnChangeListener = false, emptyStateValue = undefined } =
(config || {}) as FormCommonConfig;
const {
disabledOnChangeListener = true,
disabledOnInputListener = true,
emptyStateValue = undefined,
} = (config || {}) as FormCommonConfig;
Object.assign(DEFAULT_FORM_COMMON_CONFIG, {
disabledOnChangeListener,
disabledOnInputListener,
emptyStateValue,
});

View File

@@ -130,6 +130,11 @@ export class FormApi {
return form.values;
}
async isFieldValid(fieldName: string) {
const form = await this.getForm();
return form.isFieldValid(fieldName);
}
merge(formApi: FormApi) {
const chain = [this, formApi];
const proxy = new Proxy(formApi, {
@@ -348,4 +353,14 @@ export class FormApi {
}
return await this.submitForm();
}
async validateField(fieldName: string, opts?: Partial<ValidationOptions>) {
const form = await this.getForm();
const validateResult = await form.validateField(fieldName, opts);
if (Object.keys(validateResult?.errors ?? {}).length > 0) {
console.error('validate error', validateResult?.errors);
}
return validateResult;
}
}

View File

@@ -26,6 +26,7 @@ import { isEventObjectLike } from './helper';
interface Props extends FormSchema {}
const {
colon,
commonComponentProps,
component,
componentProps,
@@ -33,6 +34,7 @@ const {
description,
disabled,
disabledOnChangeListener,
disabledOnInputListener,
emptyStateValue,
fieldName,
formFieldProps,
@@ -227,10 +229,13 @@ function fieldBindEvent(slotProps: Record<string, any>) {
return onChange?.(e?.target?.[bindEventField] ?? e);
},
onInput: () => {},
...(disabledOnInputListener ? { onInput: undefined } : {}),
};
}
return {};
return {
...(disabledOnInputListener ? { onInput: undefined } : {}),
...(disabledOnChangeListener ? { onChange: undefined } : {}),
};
}
function createComponentProps(slotProps: Record<string, any>) {
@@ -296,7 +301,10 @@ function autofocus() {
:required="shouldRequired && !hideRequiredMark"
:style="labelStyle"
>
{{ label }}
<template v-if="label">
<span>{{ label }}</span>
<span v-if="colon" class="ml-[2px]">:</span>
</template>
</FormLabel>
<div :class="cn('relative flex w-full items-center', wrapperClass)">
<FormControl :class="cn(controlClass)">

View File

@@ -86,10 +86,12 @@ const computedSchema = computed(
formFieldProps: Record<string, any>;
} & Omit<FormSchema, 'formFieldProps'>)[] => {
const {
colon = false,
componentProps = {},
controlClass = '',
disabled,
disabledOnChangeListener = false,
disabledOnChangeListener = true,
disabledOnInputListener = true,
emptyStateValue = undefined,
formFieldProps = {},
formItemClass = '',
@@ -109,8 +111,10 @@ const computedSchema = computed(
: false;
return {
colon,
disabled,
disabledOnChangeListener,
disabledOnInputListener,
emptyStateValue,
hideLabel,
hideRequiredMark,

View File

@@ -136,6 +136,10 @@ type ComponentProps =
| MaybeComponentProps;
export interface FormCommonConfig {
/**
* 在Label后显示一个冒号
*/
colon?: boolean;
/**
* 所有表单项的props
*/
@@ -151,9 +155,14 @@ export interface FormCommonConfig {
disabled?: boolean;
/**
* 是否禁用所有表单项的change事件监听
* @default false
* @default true
*/
disabledOnChangeListener?: boolean;
/**
* 是否禁用所有表单项的input事件监听
* @default true
*/
disabledOnInputListener?: boolean;
/**
* 所有表单项的空状态值,默认都是undefinednaive-ui的空状态值是null
*/
@@ -371,6 +380,7 @@ export interface VbenFormAdapterOptions<
config?: {
baseModelPropName?: string;
disabledOnChangeListener?: boolean;
disabledOnInputListener?: boolean;
emptyStateValue?: null | undefined;
modelPropNameMap?: Partial<Record<T, string>>;
};

View File

@@ -6,7 +6,9 @@ import type { ExtendedFormApi, VbenFormProps } from './types';
import { useForwardPriorityValues } from '@vben-core/composables';
// import { isFunction } from '@vben-core/shared/utils';
import { toRaw, useTemplateRef, watch } from 'vue';
import { nextTick, onMounted, useTemplateRef, watch } from 'vue';
import { cloneDeep } from '@vben-core/shared/utils';
import { useDebounceFn } from '@vueuse/core';
@@ -59,14 +61,16 @@ function handleKeyDownEnter(event: KeyboardEvent) {
formActionsRef.value?.handleSubmit?.();
}
watch(
() => form.values,
useDebounceFn(() => {
forward.value.handleValuesChange?.(toRaw(form.values));
state.value.submitOnChange && props.formApi?.submitForm();
}, 300),
{ deep: true },
);
const handleValuesChangeDebounced = useDebounceFn((newVal) => {
forward.value.handleValuesChange?.(cloneDeep(newVal));
state.value.submitOnChange && formActionsRef.value?.handleSubmit?.();
}, 300);
onMounted(async () => {
// 只在挂载后开始监听form.values会有一个初始化的过程
await nextTick();
watch(() => form.values, handleValuesChangeDebounced, { deep: true });
});
</script>
<template>