From f73e3779ebf22cb97fd0c7ae0e2f85d9d6ccc9ad Mon Sep 17 00:00:00 2001 From: electroluxcode <3451613934@qq.com> Date: Thu, 29 Aug 2024 17:03:30 +0800 Subject: [PATCH 1/2] feat: add x-validator-merge prop && registerMergeRules api --- packages/core/src/models/Field.ts | 2 + packages/core/src/shared/internals.ts | 1 + packages/json-schema/src/compiler.ts | 5 ++- packages/json-schema/src/schema.ts | 2 + packages/json-schema/src/shared.ts | 5 +++ packages/validator/src/registry.ts | 54 +++++++++++++++++++++++++-- packages/validator/src/types.ts | 1 + 7 files changed, 65 insertions(+), 5 deletions(-) diff --git a/packages/core/src/models/Field.ts b/packages/core/src/models/Field.ts index 38812ca1f0b..424d60ff0a7 100644 --- a/packages/core/src/models/Field.ts +++ b/packages/core/src/models/Field.ts @@ -6,6 +6,7 @@ import { isArr, } from '@formily/shared' import { + Validator, ValidatorTriggerType, parseValidatorDescriptions, } from '@formily/validator' @@ -78,6 +79,7 @@ export class Field< feedbacks: IFieldFeedback[] caches: IFieldCaches = {} requests: IFieldRequests = {} + validatorMerge: Record constructor( address: FormPathPattern, props: IFieldProps, diff --git a/packages/core/src/shared/internals.ts b/packages/core/src/shared/internals.ts index ea9d586f83d..40ed5abb124 100644 --- a/packages/core/src/shared/internals.ts +++ b/packages/core/src/shared/internals.ts @@ -310,6 +310,7 @@ export const validateToFeedbacks = async ( triggerType, validateFirst: field.props.validateFirst ?? field.form.props.validateFirst, context: { field, form: field.form }, + validatorMerge: field.validatorMerge, }) batch(() => { diff --git a/packages/json-schema/src/compiler.ts b/packages/json-schema/src/compiler.ts index cc2a27c56e4..4ea608dfa17 100644 --- a/packages/json-schema/src/compiler.ts +++ b/packages/json-schema/src/compiler.ts @@ -110,7 +110,9 @@ export const patchCompile = ( } export const patchSchemaCompile = ( - targetState: IGeneralFieldState, + targetState: IGeneralFieldState & { + schema: ISchema + }, sourceSchema: ISchema, scope: any, demand = false @@ -131,4 +133,5 @@ export const patchSchemaCompile = ( patchStateFormSchema(targetState, path, compiled) } }) + targetState['schema'] = sourceSchema } diff --git a/packages/json-schema/src/schema.ts b/packages/json-schema/src/schema.ts index 72487afdebe..2b31ac159ea 100644 --- a/packages/json-schema/src/schema.ts +++ b/packages/json-schema/src/schema.ts @@ -151,6 +151,8 @@ export class Schema< ['x-display']?: Display; //校验器 ['x-validator']?: Validator; + //覆盖校验器,可以合并 全局校验 和 简写校验 + ['x-validator-merge']?: Record; //装饰器 ['x-decorator']?: Decorator; //装饰器属性 diff --git a/packages/json-schema/src/shared.ts b/packages/json-schema/src/shared.ts index 7ed112c82a4..a5c6a890af6 100644 --- a/packages/json-schema/src/shared.ts +++ b/packages/json-schema/src/shared.ts @@ -2,6 +2,7 @@ import { isFn, each, isPlainObj, isArr, toArr, FormPath } from '@formily/shared' import { isObservable, untracked } from '@formily/reactive' import { Schema } from './schema' import { ISchema } from './types' +import { registerMergeRules } from '@formily/validator' const REVA_ACTIONS_KEY = Symbol.for('__REVA_ACTIONS') @@ -36,6 +37,7 @@ export const SchemaStateMap = { 'x-display': 'display', 'x-pattern': 'pattern', 'x-validator': 'validator', + 'x-validator-merge': 'validatorMerge', 'x-decorator': 'decoratorType', 'x-component': 'componentType', 'x-decorator-props': 'decoratorProps', @@ -100,6 +102,9 @@ export const traverseSchema = ( schema: ISchema, visitor: (value: any, path: any[], omitCompile?: boolean) => void ) => { + if (schema['x-validator-merge'] !== undefined) { + registerMergeRules(schema['x-validator-merge']) + } if (schema['x-validator'] !== undefined) { visitor( schema['x-validator'], diff --git a/packages/validator/src/registry.ts b/packages/validator/src/registry.ts index b1e66880673..b1a980ec328 100644 --- a/packages/validator/src/registry.ts +++ b/packages/validator/src/registry.ts @@ -38,7 +38,14 @@ const registry = { language: getBrowserlanguage(), }, formats: {}, - rules: {}, + rules: { + // 全局校验规则 + global: {}, + // 简写校验规则 + easy: {}, + // 自定义校验规则 + merge: {}, + }, template: null, } @@ -88,21 +95,60 @@ export const getValidateMessageTemplateEngine = () => registry.template export const getValidateFormats = (key?: string) => key ? registry.formats[key] : registry.formats +/** + * @description 覆盖规则 + * 全局规则 < 简写规则 < 用户自定义规则 + * @param key + * @returns + */ export const getValidateRules = ( key?: T ): T extends string ? ValidatorFunction - : { [key: string]: ValidatorFunction } => - key ? registry.rules[key as any] : registry.rules + : { [key: string]: ValidatorFunction } => { + let rules = { + ...registry.rules['global'], + ...registry.rules['easy'], + ...registry.rules['merge'], + } + return key ? rules[key as any] : rules +} export const registerValidateLocale = (locale: IRegistryLocales) => { registry.locales.messages = deepmerge(registry.locales.messages, locale) } +/** + * @description 全局规则 + * @param rules + */ export const registerValidateRules = (rules: IRegistryRules) => { each(rules, (rule, key) => { if (isFn(rule)) { - registry.rules[key] = rule + registry.rules['global'][key] = rule + } + }) +} +/** + * @description 简写规则 + * @param rules + */ +export const registerEasyRules = (rules: IRegistryRules) => { + each(rules, (rule, key) => { + if (isFn(rule)) { + registry.rules['easy'][key] = rule + } + }) +} + +/** + * @description 自定义规则 + * @param rules + */ +export const registerMergeRules = (rules: IRegistryRules) => { + each(rules, (rule, key) => { + if (isFn(rule)) { + registry.rules['merge'][key] = rule } }) } diff --git a/packages/validator/src/types.ts b/packages/validator/src/types.ts index 13deea9c00d..bd897ca32fc 100644 --- a/packages/validator/src/types.ts +++ b/packages/validator/src/types.ts @@ -111,4 +111,5 @@ export interface IValidatorOptions { validateFirst?: boolean triggerType?: ValidatorTriggerType context?: Context + validatorMerge?: Record } From 2de57ade1bfea2096062ce34795e66c701dd91e5 Mon Sep 17 00:00:00 2001 From: electroluxcode <3451613934@qq.com> Date: Thu, 29 Aug 2024 17:04:19 +0800 Subject: [PATCH 2/2] test: add test case --- .../validator/src/__tests__/validator.spec.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/validator/src/__tests__/validator.spec.ts b/packages/validator/src/__tests__/validator.spec.ts index 2fbf5dba344..2bf58821294 100644 --- a/packages/validator/src/__tests__/validator.spec.ts +++ b/packages/validator/src/__tests__/validator.spec.ts @@ -4,12 +4,22 @@ import { registerValidateFormats, setValidateLanguage, registerValidateMessageTemplateEngine, + registerMergeRules, } from '../index' registerValidateRules({ custom: (value) => (value === '123' ? 'custom error' : ''), customBool: () => false, customBool2: () => true, + customBool3: (value) => { + return value === 'registerValidateRules' + }, +}) + +registerMergeRules({ + customBool3: (value) => { + return value === 'registerMergeRules' + }, }) registerValidateFormats({ @@ -531,3 +541,39 @@ test('validator order with format', async () => { 'The field value is required' ) }) + +test('validator merge', async () => { + registerMergeRules({ + customBool3: (value) => { + return value === 'registerMergeRules' + }, + }) + noError( + await validate('registerMergeRules', { + customBool3: true, + message: 'custom error', + }) + ) +}) + +/** + * @description other test case should be before this one, because it will change the default rules + */ +test('validator merge required', async () => { + registerMergeRules({}) + hasError( + await validate('', [{ required: true }]), + 'The field value is required' + ) + + registerMergeRules({ + required: () => { + return true + }, + }) + noError( + await validate('', { + required: true, + }) + ) +})