feat: form validator (#569)
* feat: form validator * fix: max can equal min * feat: adjust input validation rule * feat: improve field validation * feat: optimize field validation * feat: improve field validation * fix: percent validation Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
68d35cf597
commit
2282ec1a2d
@ -5,6 +5,7 @@ import React, { useEffect } from 'react';
|
|||||||
import { useCompile, useComponent, useFormBlockContext } from '..';
|
import { useCompile, useComponent, useFormBlockContext } from '..';
|
||||||
import { CollectionFieldProvider } from './CollectionFieldProvider';
|
import { CollectionFieldProvider } from './CollectionFieldProvider';
|
||||||
import { useCollectionField } from './hooks';
|
import { useCollectionField } from './hooks';
|
||||||
|
import { concat } from 'lodash';
|
||||||
|
|
||||||
// TODO: 初步适配
|
// TODO: 初步适配
|
||||||
const InternalField: React.FC = (props) => {
|
const InternalField: React.FC = (props) => {
|
||||||
@ -38,8 +39,9 @@ const InternalField: React.FC = (props) => {
|
|||||||
setFieldProps('title', uiSchema.title);
|
setFieldProps('title', uiSchema.title);
|
||||||
setFieldProps('description', uiSchema.description);
|
setFieldProps('description', uiSchema.description);
|
||||||
setFieldProps('initialValue', uiSchema.default);
|
setFieldProps('initialValue', uiSchema.default);
|
||||||
if (!field.validator && uiSchema['x-validator']) {
|
if (!field.validator && (uiSchema['x-validator'] || fieldSchema['x-validator'])) {
|
||||||
field.validator = uiSchema['x-validator'];
|
const concatSchema = concat([], uiSchema['x-validator'] || [], fieldSchema['x-validator'] || [])
|
||||||
|
field.validator = concatSchema;
|
||||||
}
|
}
|
||||||
if (fieldSchema['x-disabled'] === true) {
|
if (fieldSchema['x-disabled'] === true) {
|
||||||
field.disabled = true;
|
field.disabled = true;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps, operators } from './properties';
|
import { defaultProps, operators } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
export const input: IField = {
|
export const input: IField = {
|
||||||
name: 'input',
|
name: 'input',
|
||||||
@ -29,4 +30,110 @@ export const input: IField = {
|
|||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
max: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Max length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.min').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Max length must greater than min length')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Min length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.max'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Min length must less than max length')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
len: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
format: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Format") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
enum: [{
|
||||||
|
label: '{{ t("url") }}',
|
||||||
|
value: 'url',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("email") }}',
|
||||||
|
value: 'email',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("ipv6") }}',
|
||||||
|
value: 'ipv6',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("ipv4") }}',
|
||||||
|
value: 'ipv4',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("number") }}',
|
||||||
|
value: 'number',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("integer") }}',
|
||||||
|
value: 'integer',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("idcard") }}',
|
||||||
|
value: 'idcard',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("qq") }}',
|
||||||
|
value: 'qq',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("phone") }}',
|
||||||
|
value: 'phone',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("money") }}',
|
||||||
|
value: 'money',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("zh") }}',
|
||||||
|
value: 'zh',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("date") }}',
|
||||||
|
value: 'date',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("zip") }}',
|
||||||
|
value: 'zip',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Regular expression") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
prefix: '/',
|
||||||
|
suffix: '/',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
import { defaultProps, operators } from './properties';
|
import { defaultProps, operators } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
import { registerValidateFormats } from '@formily/core';
|
||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
|
||||||
|
registerValidateFormats({
|
||||||
|
odd: /^-?\d*[13579]$/,
|
||||||
|
even: /^-?\d*[02468]$/
|
||||||
|
});
|
||||||
|
|
||||||
export const integer: IField = {
|
export const integer: IField = {
|
||||||
name: 'integer',
|
name: 'integer',
|
||||||
@ -28,4 +36,65 @@ export const integer: IField = {
|
|||||||
filterable: {
|
filterable: {
|
||||||
operators: operators.number,
|
operators: operators.number,
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
maximum: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Maximum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.minimum').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Maximum must greater than minimum')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
minimum: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Minimum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.maximum'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Minimum must less than maximum')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
format: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Format") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
enum: [{
|
||||||
|
label: '{{ t("Odd") }}',
|
||||||
|
value: 'odd',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("Even") }}',
|
||||||
|
value: 'even',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Regular expression") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
prefix: '/',
|
||||||
|
suffix: '/',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps } from './properties';
|
import { defaultProps } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n'
|
||||||
|
|
||||||
export const markdown: IField = {
|
export const markdown: IField = {
|
||||||
name: 'markdown',
|
name: 'markdown',
|
||||||
@ -25,4 +26,41 @@ export const markdown: IField = {
|
|||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
max: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Max length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.min').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Max length must greater than min length')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Min length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.max'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Min length must less than max length')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import { registerValidateRules } from '@formily/core';
|
||||||
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps, operators } from './properties';
|
import { defaultProps, operators } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
export const number: IField = {
|
export const number: IField = {
|
||||||
name: 'number',
|
name: 'number',
|
||||||
@ -42,4 +45,62 @@ export const number: IField = {
|
|||||||
filterable: {
|
filterable: {
|
||||||
operators: operators.number,
|
operators: operators.number,
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
maximum: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Maximum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.minimum').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Maximum must greater than minimum')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
minimum: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Minimum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.maximum'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Minimum must less than maximum')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
format: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Format") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
enum: [{
|
||||||
|
label: '{{ t("Integer") }}',
|
||||||
|
value: 'integer',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("Odd") }}',
|
||||||
|
value: 'odd',
|
||||||
|
}, {
|
||||||
|
label: '{{ t("Even") }}',
|
||||||
|
value: 'even',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Regular expression") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
prefix: '/',
|
||||||
|
suffix: '/',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps } from './properties';
|
import { defaultProps } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
export const password: IField = {
|
export const password: IField = {
|
||||||
name: 'password',
|
name: 'password',
|
||||||
@ -19,4 +21,41 @@ export const password: IField = {
|
|||||||
properties: {
|
properties: {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
max: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Max length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.min').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Max length must greater than min length')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Min length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.max'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Min length must less than max length')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,51 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps, operators } from './properties';
|
import { defaultProps, operators } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
import { registerValidateFormats, registerValidateRules, registerValidateLocale } from '@formily/core';
|
||||||
|
|
||||||
|
registerValidateRules({
|
||||||
|
percentMode(value, rule) {
|
||||||
|
const { maxValue, minValue } = rule;
|
||||||
|
|
||||||
|
if (maxValue) {
|
||||||
|
if (value > maxValue) {
|
||||||
|
return {
|
||||||
|
type: 'error',
|
||||||
|
message: `${i18n.t('The field value cannot be greater than ')}${maxValue * 100}%`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minValue) {
|
||||||
|
if (value < minValue) {
|
||||||
|
return {
|
||||||
|
type: 'error',
|
||||||
|
message: `${i18n.t('The field value cannot be less than ')}${minValue * 100}%`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
percentFormats(value, rule) {
|
||||||
|
const { percentFormat } = rule;
|
||||||
|
|
||||||
|
if (value && percentFormat === 'Integer' && /^-?[1-9]\d*$/.test((value * 100).toString()) === false) {
|
||||||
|
return {
|
||||||
|
type: 'error',
|
||||||
|
message: `${i18n.t('The field value is not an integer number')}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// registerValidateFormats({
|
||||||
|
// percentInteger: /^(\d+)(.\d{0,2})?$/,
|
||||||
|
// });
|
||||||
|
|
||||||
export const percent: IField = {
|
export const percent: IField = {
|
||||||
name: 'percent',
|
name: 'percent',
|
||||||
@ -43,4 +89,62 @@ export const percent: IField = {
|
|||||||
filterable: {
|
filterable: {
|
||||||
operators: operators.number,
|
operators: operators.number,
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
maxValue: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Maximum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Percent',
|
||||||
|
'x-component-props': {
|
||||||
|
addonAfter: '%',
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.minimum').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Maximum must greater than minimum')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
minValue: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Minimum") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Percent',
|
||||||
|
'x-component-props': {
|
||||||
|
addonAfter: '%',
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.maximum'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Minimum must less than maximum')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
percentFormat: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Format") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
enum: [{
|
||||||
|
label: '{{ t("Integer") }}',
|
||||||
|
value: 'Integer',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
pattern: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Regular expression") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
prefix: '/',
|
||||||
|
suffix: '/',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { ISchema } from '@formily/react';
|
import type { ISchema } from '@formily/react';
|
||||||
import { defaultProps } from './properties';
|
import { defaultProps } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
export const richText: IField = {
|
export const richText: IField = {
|
||||||
name: 'richText',
|
name: 'richText',
|
||||||
@ -26,4 +27,41 @@ export const richText: IField = {
|
|||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema, formItemStyle) {
|
||||||
|
return {
|
||||||
|
max: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Max length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.min').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Max length must greater than min length')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Min length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.max'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Min length must less than max length')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { defaultProps } from './properties';
|
import { defaultProps } from './properties';
|
||||||
import { IField } from './types';
|
import { IField } from './types';
|
||||||
|
import { i18n } from '../../i18n';
|
||||||
|
|
||||||
export const textarea: IField = {
|
export const textarea: IField = {
|
||||||
name: 'textarea',
|
name: 'textarea',
|
||||||
@ -26,4 +27,41 @@ export const textarea: IField = {
|
|||||||
schema['x-component-props']['ellipsis'] = true;
|
schema['x-component-props']['ellipsis'] = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
validateSchema(fieldSchema) {
|
||||||
|
return {
|
||||||
|
max: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Max length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': `{{(field) => {
|
||||||
|
const targetValue = field.query('.min').value();
|
||||||
|
field.selfErrors =
|
||||||
|
!!targetValue && !!field.value && targetValue > field.value ? '${i18n.t('Max length must greater than min length')}' : ''
|
||||||
|
}}}`,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: 'number',
|
||||||
|
title: '{{ t("Min length") }}',
|
||||||
|
minimum: 0,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'InputNumber',
|
||||||
|
'x-component-props': {
|
||||||
|
precision: 0
|
||||||
|
},
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['.max'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
selfErrors: `{{!!$deps[0] && !!$self.value && $deps[0] < $self.value ? '${i18n.t('Min length must less than max length')}' : ''}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -549,4 +549,5 @@ export default {
|
|||||||
"Field component": "Field component",
|
"Field component": "Field component",
|
||||||
"Subtable": "Subtable",
|
"Subtable": "Subtable",
|
||||||
"Subform": "Subform",
|
"Subform": "Subform",
|
||||||
|
"Regular expression": "Pattern",
|
||||||
}
|
}
|
||||||
|
@ -236,6 +236,25 @@ export default {
|
|||||||
"Default is the ID field": "默认为 ID 字段",
|
"Default is the ID field": "默认为 ID 字段",
|
||||||
"Set default sorting rules": "设置排序规则",
|
"Set default sorting rules": "设置排序规则",
|
||||||
|
|
||||||
|
"Set validation rules": "设置验证规则",
|
||||||
|
"Max length": "最大长度",
|
||||||
|
"Min length": "最小长度",
|
||||||
|
"Maximum": "最大值",
|
||||||
|
"Minimum": "最小值",
|
||||||
|
"Max length must greater than min length": "最大长度必须大于最小长度",
|
||||||
|
"Min length must less than max length": "最小长度必须小于最大长度",
|
||||||
|
"Maximum must greater than minimum": "最大值必须大于最小值",
|
||||||
|
"Minimum must less than maximum": "最小值必须小于最大值",
|
||||||
|
"Validation rule": "验证规则",
|
||||||
|
"Add validation rule": "新增验证规则",
|
||||||
|
"Format": "格式",
|
||||||
|
"Regular expression": "正则表达式",
|
||||||
|
"Error message": "错误消息",
|
||||||
|
"Length": "长度",
|
||||||
|
"The field value cannot be greater than ": "数值不能大于",
|
||||||
|
"The field value cannot be less than ": "数值不能小于",
|
||||||
|
"The field value is not an integer number": "数字不是整数",
|
||||||
|
|
||||||
"is before": "早于",
|
"is before": "早于",
|
||||||
"is after": "晚于",
|
"is after": "晚于",
|
||||||
"is on or after": "不早于",
|
"is on or after": "不早于",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { FormItem as Item } from '@formily/antd';
|
import { ArrayCollapse, FormItem as Item, FormLayout } from '@formily/antd';
|
||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
@ -11,6 +11,7 @@ import { useCollection, useCollectionManager } from '../../../collection-manager
|
|||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||||
import { BlockItem } from '../block-item';
|
import { BlockItem } from '../block-item';
|
||||||
import { HTMLEncode } from '../input/shared';
|
import { HTMLEncode } from '../input/shared';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
const divWrap = (schema: ISchema) => {
|
const divWrap = (schema: ISchema) => {
|
||||||
return {
|
return {
|
||||||
@ -59,6 +60,7 @@ FormItem.Designer = (props) => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
|
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
|
||||||
const interfaceConfig = getInterface(collectionField?.interface);
|
const interfaceConfig = getInterface(collectionField?.interface);
|
||||||
|
const validateSchema = interfaceConfig?.['validateSchema']?.(fieldSchema);
|
||||||
const originalTitle = collectionField?.uiSchema?.title;
|
const originalTitle = collectionField?.uiSchema?.title;
|
||||||
const targetFields = collectionField?.target ? getCollectionFields(collectionField.target) : [];
|
const targetFields = collectionField?.target ? getCollectionFields(collectionField.target) : [];
|
||||||
const isSubFormAssocitionField = field.address.segments.includes('__form_grid');
|
const isSubFormAssocitionField = field.address.segments.includes('__form_grid');
|
||||||
@ -81,6 +83,7 @@ FormItem.Designer = (props) => {
|
|||||||
if (fieldSchema['x-read-pretty'] === true) {
|
if (fieldSchema['x-read-pretty'] === true) {
|
||||||
readOnlyMode = 'read-pretty';
|
readOnlyMode = 'read-pretty';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GeneralSchemaDesigner>
|
<GeneralSchemaDesigner>
|
||||||
{collectionField && (
|
{collectionField && (
|
||||||
@ -201,6 +204,126 @@ FormItem.Designer = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{validateSchema && (
|
||||||
|
<SchemaSettings.ModalItem
|
||||||
|
title={t('Set validation rules')}
|
||||||
|
components={{ ArrayCollapse, FormLayout }}
|
||||||
|
schema={{
|
||||||
|
type: 'object',
|
||||||
|
title: t('Set validation rules'),
|
||||||
|
properties: {
|
||||||
|
rules: {
|
||||||
|
type: 'array',
|
||||||
|
default: fieldSchema?.['x-validator'],
|
||||||
|
'x-component': 'ArrayCollapse',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component-props': {
|
||||||
|
accordion: true,
|
||||||
|
},
|
||||||
|
maxItems: 3,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'ArrayCollapse.CollapsePanel',
|
||||||
|
'x-component-props': {
|
||||||
|
header: '{{ t("Validation rule") }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
index: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.Index',
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'FormLayout',
|
||||||
|
'x-component-props': {
|
||||||
|
labelStyle: {
|
||||||
|
marginTop: '6px',
|
||||||
|
},
|
||||||
|
labelCol: 8,
|
||||||
|
wrapperCol: 16,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
...validateSchema,
|
||||||
|
message: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{ t("Error message") }}',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Input.TextArea',
|
||||||
|
'x-component-props': {
|
||||||
|
autoSize: {
|
||||||
|
minRows: 2,
|
||||||
|
maxRows: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.Remove',
|
||||||
|
},
|
||||||
|
moveUp: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.MoveUp',
|
||||||
|
},
|
||||||
|
moveDown: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayCollapse.MoveDown',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Add validation rule") }}',
|
||||||
|
'x-component': 'ArrayCollapse.Addition',
|
||||||
|
'x-reactions': {
|
||||||
|
dependencies: ['rules'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
disabled: '{{$deps[0].length >= 3}}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as ISchema}
|
||||||
|
onSubmit={(v) => {
|
||||||
|
const rules = [];
|
||||||
|
for (const rule of v.rules) {
|
||||||
|
rules.push(_.pickBy(rule, _.identity))
|
||||||
|
}
|
||||||
|
const schema = {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
};
|
||||||
|
// return;
|
||||||
|
// if (['number'].includes(collectionField?.interface) && collectionField?.uiSchema?.['x-component-props']?.['stringMode'] === true) {
|
||||||
|
// rules['numberStringMode'] = true;
|
||||||
|
// }
|
||||||
|
if (['percent'].includes(collectionField?.interface)) {
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (!!rule.maxValue || !!rule.minValue) {
|
||||||
|
rule['percentMode'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.percentFormat) {
|
||||||
|
rule['percentFormats'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const concatValidator = _.concat([], collectionField?.uiSchema?.['x-validator'] || [], rules)
|
||||||
|
field.validator = concatValidator;
|
||||||
|
fieldSchema['x-validator'] = rules;
|
||||||
|
schema['x-validator'] = rules;
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema,
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{form && !isSubFormAssocitionField && ['o2o', 'oho', 'obo', 'o2m'].includes(collectionField?.interface) && (
|
{form && !isSubFormAssocitionField && ['o2o', 'oho', 'obo', 'o2m'].includes(collectionField?.interface) && (
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.SelectItem
|
||||||
title={t('Field component')}
|
title={t('Field component')}
|
||||||
|
@ -3,6 +3,12 @@ import { connect, mapReadPretty } from '@formily/react';
|
|||||||
import { InputNumber as AntdNumber } from 'antd';
|
import { InputNumber as AntdNumber } from 'antd';
|
||||||
import { ReadPretty } from './ReadPretty';
|
import { ReadPretty } from './ReadPretty';
|
||||||
|
|
||||||
export const InputNumber = connect(AntdNumber, mapReadPretty(ReadPretty));
|
export const InputNumber = connect((props) => {
|
||||||
|
const { onChange, ...others } = props;
|
||||||
|
const handleChange = (v) => {
|
||||||
|
onChange(parseFloat(v));
|
||||||
|
}
|
||||||
|
return (<AntdNumber onChange={handleChange} {...others} />);
|
||||||
|
}, mapReadPretty(ReadPretty));
|
||||||
|
|
||||||
export default InputNumber;
|
export default InputNumber;
|
||||||
|
@ -12,10 +12,10 @@ export const Percent = connect(
|
|||||||
<InputNumber
|
<InputNumber
|
||||||
{...props}
|
{...props}
|
||||||
addonAfter="%"
|
addonAfter="%"
|
||||||
value={math.round(value * 100, 9)}
|
value={value ? math.round(value * 100, 9) : null}
|
||||||
onChange={(v: any) => {
|
onChange={(v: any) => {
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(math.round(v / 100, 9));
|
onChange(v ? math.round(v / 100, 9) : null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -23,7 +23,6 @@ const useTableColumns = () => {
|
|||||||
const field = useField<ArrayField>();
|
const field = useField<ArrayField>();
|
||||||
const schema = useFieldSchema();
|
const schema = useFieldSchema();
|
||||||
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
||||||
// console.log('useTableColumns', exists);
|
|
||||||
const columns = schema
|
const columns = schema
|
||||||
.reduceProperties((buf, s) => {
|
.reduceProperties((buf, s) => {
|
||||||
if (isColumnComponent(s)) {
|
if (isColumnComponent(s)) {
|
||||||
@ -38,7 +37,6 @@ const useTableColumns = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
const dataIndex = collectionFields?.length > 0 ? collectionFields[0].name : s.name;
|
const dataIndex = collectionFields?.length > 0 ? collectionFields[0].name : s.name;
|
||||||
console.log('useTableColumns', s.name, s, field.value);
|
|
||||||
return {
|
return {
|
||||||
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
title: <RecursionField name={s.name} schema={s} onlyRenderSelf />,
|
||||||
dataIndex,
|
dataIndex,
|
||||||
@ -48,7 +46,6 @@ const useTableColumns = () => {
|
|||||||
render: (v, record) => {
|
render: (v, record) => {
|
||||||
const index = field.value?.indexOf(record);
|
const index = field.value?.indexOf(record);
|
||||||
// console.log((Date.now() - start) / 1000);
|
// console.log((Date.now() - start) / 1000);
|
||||||
console.log('useTableColumns.index', index, record);
|
|
||||||
return (
|
return (
|
||||||
<RecordIndexProvider index={index}>
|
<RecordIndexProvider index={index}>
|
||||||
<RecordProvider record={record}>
|
<RecordProvider record={record}>
|
||||||
|
Loading…
Reference in New Issue
Block a user