fix(@vben/web-antdv-next): 更新表单验证逻辑,使用 Promise 处理异步校验

pull/364/head
XuZhiqiang 2026-06-18 16:32:12 +08:00
parent d8c8775af2
commit 1f7d21d8e6
6 changed files with 57 additions and 80 deletions

View File

@ -1,4 +1,4 @@
import type { Rule } from 'antdv-next/es/form';
import type { FormItemProps } from 'antdv-next';
import type { PageParam, PageResult } from '@vben/request';
@ -6,6 +6,8 @@ import { isEmpty } from '@vben/utils';
import { requestClient } from '#/api/request';
type FormItemRule = NonNullable<FormItemProps['rules']>[number];
export namespace ThingModelApi {
/** IoT 物模型数据 */
export interface ThingModel {
@ -95,46 +97,42 @@ export namespace ThingModelApi {
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
function buildRequiredNumberValidator(label: string) {
return (_rule: any, value: any, callback: any) => {
return (_rule: any, value: any) => {
if (isEmpty(value)) {
callback(new Error(`${label}不能为空`));
return;
return Promise.reject(new Error(`${label}不能为空`));
}
if (Number.isNaN(Number(value))) {
callback(new Error(`${label}必须是数字`));
return;
return Promise.reject(new Error(`${label}必须是数字`));
}
callback();
return Promise.resolve();
};
}
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
export function buildIdentifierLikeNameValidator(label: string) {
return (_rule: any, value: string, callback: any) => {
return (_rule: any, value: string) => {
if (isEmpty(value)) {
callback(new Error(`${label}不能为空`));
return;
return Promise.reject(new Error(`${label}不能为空`));
}
if (!/^[一-龥A-Za-z0-9]/.test(value)) {
callback(new Error(`${label}必须以中文、英文字母或数字开头`));
return;
return Promise.reject(
new Error(`${label}必须以中文、英文字母或数字开头`),
);
}
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
callback(
return Promise.reject(
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
);
return;
}
if (value.length > 20) {
callback(new Error(`${label}长度不能超过 20 个字符`));
return;
return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
}
callback();
return Promise.resolve();
};
}
/** IoT 物模型表单校验规则 */
export const ThingModelFormRules: Record<string, Rule[]> = {
export const ThingModelFormRules: Record<string, FormItemRule[]> = {
name: [
{ required: true, message: '功能名称不能为空', trigger: 'blur' },
{
@ -153,7 +151,7 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
trigger: 'blur',
},
{
validator: (_rule: any, value: string, callback: any) => {
validator: (_rule: any, value: string) => {
const reservedKeywords = [
'set',
'get',
@ -164,18 +162,16 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
'value',
];
if (reservedKeywords.includes(value)) {
callback(
return Promise.reject(
new Error(
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
),
);
return;
}
if (/^\d+$/.test(value)) {
callback(new Error('标识符不能是纯数字'));
return;
return Promise.reject(new Error('标识符不能是纯数字'));
}
callback();
return Promise.resolve();
},
trigger: 'blur',
},

View File

@ -71,20 +71,18 @@ const rules: Record<string, Rule[]> = {
key: [
{ required: true, message: '流程标识不能为空', trigger: 'blur' },
{
validator: (_rule: any, value: string, callback: any) => {
validator: (_rule: any, value: string) => {
if (!value) {
callback();
return;
return Promise.resolve();
}
if (!/^[a-z_][-\w.$]*$/i.test(value)) {
callback(
return Promise.reject(
new Error(
'只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头',
),
);
return;
}
callback();
return Promise.resolve();
},
trigger: 'blur',
},

View File

@ -122,23 +122,21 @@ function normalizeFormData(result: any): RuleSceneApi.SceneRule {
}
/** 触发器校验 */
function validateTriggers(_rule: any, value: any, callback: any) {
function validateTriggers(_rule: any, value: any) {
const error = validateSceneRuleTriggers(value);
if (error) {
callback(new Error(error));
return;
return Promise.reject(new Error(error));
}
callback();
return Promise.resolve();
}
/** 执行器校验 */
function validateActions(_rule: any, value: any, callback: any) {
function validateActions(_rule: any, value: any) {
const error = validateSceneRuleActions(value);
if (error) {
callback(new Error(error));
return;
return Promise.reject(new Error(error));
}
callback();
return Promise.resolve();
}
const formRules = reactive({

View File

@ -35,51 +35,44 @@ function deleteEnum(index: number) {
}
/** 校验单项枚举值:必填、数字、不重复 */
function validateEnumValue(_rule: any, value: any, callback: any) {
function validateEnumValue(_rule: any, value: any) {
if (isEmpty(value)) {
callback(new Error('枚举值不能为空'));
return;
return Promise.reject(new Error('枚举值不能为空'));
}
if (Number.isNaN(Number(value))) {
callback(new Error('枚举值必须是数字'));
return;
return Promise.reject(new Error('枚举值必须是数字'));
}
const sameCount = dataSpecsList.value.filter(
(it) => it.value === value,
).length;
if (sameCount > 1) {
callback(new Error('枚举值不能重复'));
return;
return Promise.reject(new Error('枚举值不能重复'));
}
callback();
return Promise.resolve();
}
/** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */
function validateEnumList(_rule: any, _value: any, callback: any) {
function validateEnumList(_rule: any, _value: any) {
if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个枚举项'));
return;
return Promise.reject(new Error('请至少添加一个枚举项'));
}
const hasEmpty = dataSpecsList.value.some(
(item) => isEmpty(item.value) || isEmpty(item.name),
);
if (hasEmpty) {
callback(new Error('存在未填写的枚举值或描述'));
return;
return Promise.reject(new Error('存在未填写的枚举值或描述'));
}
const hasInvalidNumber = dataSpecsList.value.some((item) =>
Number.isNaN(Number(item.value)),
);
if (hasInvalidNumber) {
callback(new Error('存在非数字的枚举值'));
return;
return Promise.reject(new Error('存在非数字的枚举值'));
}
const values = dataSpecsList.value.map((item) => item.value);
if (new Set(values).size !== values.length) {
callback(new Error('存在重复的枚举值'));
return;
return Promise.reject(new Error('存在重复的枚举值'));
}
callback();
return Promise.resolve();
}
</script>

View File

@ -36,53 +36,46 @@ function unitChange(unitSpecs: any) {
}
/** 校验最小值 */
function validateMin(_rule: any, _value: any, callback: any) {
function validateMin(_rule: any, _value: any) {
const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max);
if (Number.isNaN(min)) {
callback(new Error('请输入有效的数值'));
return;
return Promise.reject(new Error('请输入有效的数值'));
}
if (!Number.isNaN(max) && min >= max) {
callback(new Error('最小值必须小于最大值'));
return;
return Promise.reject(new Error('最小值必须小于最大值'));
}
callback();
return Promise.resolve();
}
/** 校验最大值 */
function validateMax(_rule: any, _value: any, callback: any) {
function validateMax(_rule: any, _value: any) {
const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max);
if (Number.isNaN(max)) {
callback(new Error('请输入有效的数值'));
return;
return Promise.reject(new Error('请输入有效的数值'));
}
if (!Number.isNaN(min) && max <= min) {
callback(new Error('最大值必须大于最小值'));
return;
return Promise.reject(new Error('最大值必须大于最小值'));
}
callback();
return Promise.resolve();
}
/** 校验步长 */
function validateStep(_rule: any, _value: any, callback: any) {
function validateStep(_rule: any, _value: any) {
const step = Number(dataSpecs.value.step);
if (Number.isNaN(step)) {
callback(new Error('请输入有效的数值'));
return;
return Promise.reject(new Error('请输入有效的数值'));
}
if (step <= 0) {
callback(new Error('步长必须大于 0'));
return;
return Promise.reject(new Error('步长必须大于 0'));
}
const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max);
if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) {
callback(new Error('步长不能大于最大值与最小值的差值'));
return;
return Promise.reject(new Error('步长不能大于最大值与最小值的差值'));
}
callback();
return Promise.resolve();
}
</script>

View File

@ -33,12 +33,11 @@ const structFormRef = ref();
const formData = ref<any>(buildEmptyFormData());
/** 校验结构体属性对象非空 */
function validateStructSpecsList(_rule: any, _value: any, callback: any) {
function validateStructSpecsList(_rule: any, _value: any) {
if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个结构体属性对象'));
return;
return Promise.reject(new Error('请至少添加一个结构体属性对象'));
}
callback();
return Promise.resolve();
}
const [Modal, modalApi] = useVbenModal({