mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-09-19 02:04:37 +08:00
fix: add missing form validation rules and centralize regex patterns
- Add required validation for command type, call type, timeout, event type/level, menu type/level, temperature range - Add phone (11-digit) and email format validation on user form - Centralize NAME_PATTERN, AUTH_NAME_PATTERN, PHONE_PATTERN, EMAIL_PATTERN into formRuleUtil and align with backend patterns - Apply centralized patterns across all settings edit forms (group, label, menu, resource, role, user, command, event, model config)
This commit is contained in:
@@ -488,6 +488,9 @@ export default {
|
||||
defaultValue: 'Default',
|
||||
enabled: 'Enabled',
|
||||
nameRequired: 'Command name is required',
|
||||
commandTypeRequired: 'Please select command type',
|
||||
callTypeRequired: 'Please select call type',
|
||||
timeoutRequired: 'Please enter timeout',
|
||||
codeRequired: 'Command code is required',
|
||||
paramRequired: 'Command param name, code, direction, and type are required.',
|
||||
paramNamePattern:
|
||||
@@ -554,6 +557,8 @@ export default {
|
||||
type: 'Type',
|
||||
enabled: 'Enabled',
|
||||
nameRequired: 'Event name is required',
|
||||
eventTypeRequired: 'Please select event type',
|
||||
eventLevelRequired: 'Please select event level',
|
||||
codeRequired: 'Event code is required',
|
||||
paramRequired: 'Event param name, code, and type are required.',
|
||||
paramNamePattern:
|
||||
@@ -971,7 +976,9 @@ export default {
|
||||
userNamePlaceholder: 'Enter user name',
|
||||
nickNamePlaceholder: 'Enter nickname',
|
||||
phonePlaceholder: 'Enter phone number',
|
||||
phoneFormat: 'Please enter a valid 11-digit phone number',
|
||||
emailPlaceholder: 'Enter email',
|
||||
emailFormat: 'Please enter a valid email format',
|
||||
confirmDelete: 'Are you sure to delete this user? This action cannot be undone!',
|
||||
assignSaved: 'Role assignment saved',
|
||||
empty: 'No user data!',
|
||||
@@ -1067,6 +1074,8 @@ export default {
|
||||
titleZhPlaceholder: 'Enter Chinese display title',
|
||||
titleEnPlaceholder: 'Enter English display title',
|
||||
parentMenuIdPlaceholder: 'Optional, default top-level',
|
||||
menuTypeRequired: 'Please select menu type',
|
||||
menuLevelRequired: 'Please select menu level',
|
||||
confirmDelete: 'Are you sure to delete this menu? This action cannot be undone!',
|
||||
empty: 'No menu data!',
|
||||
},
|
||||
@@ -1129,6 +1138,7 @@ export default {
|
||||
modelRequired: 'Model is required',
|
||||
providerRequired: 'Provider is required',
|
||||
maxTokensRequired: 'Max tokens is required',
|
||||
temperatureRange: 'Temperature must be between 0.0 and 2.0',
|
||||
nameRequired: 'Name is required',
|
||||
baseUrlRequired: 'Base URL is required',
|
||||
confirmDeleteModel: 'Delete model {name}?',
|
||||
|
||||
@@ -487,6 +487,9 @@ export default {
|
||||
defaultValue: '默认值',
|
||||
enabled: '启用',
|
||||
nameRequired: '指令名称不能为空',
|
||||
commandTypeRequired: '请选择指令类型',
|
||||
callTypeRequired: '请选择调用类型',
|
||||
timeoutRequired: '请输入超时时间',
|
||||
codeRequired: '指令标识不能为空',
|
||||
paramRequired: '指令参数名称、标识、方向和类型不能为空。',
|
||||
paramNamePattern: '指令参数名称需2-32位,以字母、数字或中文开头,仅支持字母、数字、中文及-_#@/.|。',
|
||||
@@ -552,6 +555,8 @@ export default {
|
||||
type: '类型',
|
||||
enabled: '启用',
|
||||
nameRequired: '事件名称不能为空',
|
||||
eventTypeRequired: '请选择事件类型',
|
||||
eventLevelRequired: '请选择事件等级',
|
||||
codeRequired: '事件标识不能为空',
|
||||
paramRequired: '事件参数名称、标识和类型不能为空。',
|
||||
paramNamePattern: '事件参数名称需2-32位,以字母、数字或中文开头,仅支持字母、数字、中文及-_#@/.|。',
|
||||
@@ -968,7 +973,9 @@ export default {
|
||||
userNamePlaceholder: '请输入用户名',
|
||||
nickNamePlaceholder: '请输入昵称',
|
||||
phonePlaceholder: '请输入手机号',
|
||||
phoneFormat: '请输入正确的11位手机号',
|
||||
emailPlaceholder: '请输入邮箱',
|
||||
emailFormat: '请输入正确的邮箱格式',
|
||||
confirmDelete: '是否确定删除该用户? 该操作不可恢复!',
|
||||
assignSaved: '角色分配已保存',
|
||||
empty: '暂无用户数据!',
|
||||
@@ -1064,6 +1071,8 @@ export default {
|
||||
titleZhPlaceholder: '请输入中文显示名称',
|
||||
titleEnPlaceholder: '请输入英文显示名称',
|
||||
parentMenuIdPlaceholder: '选填, 默认顶级菜单',
|
||||
menuTypeRequired: '请选择菜单类型',
|
||||
menuLevelRequired: '请选择菜单层级',
|
||||
confirmDelete: '是否确定删除该菜单? 该操作不可恢复!',
|
||||
empty: '暂无菜单数据!',
|
||||
},
|
||||
@@ -1126,6 +1135,7 @@ export default {
|
||||
modelRequired: '模型不能为空',
|
||||
providerRequired: '供应商不能为空',
|
||||
maxTokensRequired: '最大词元不能为空',
|
||||
temperatureRange: '温度值需在 0.0~2.0 之间',
|
||||
nameRequired: '名称不能为空',
|
||||
baseUrlRequired: 'Base URL 不能为空',
|
||||
confirmDeleteModel: '是否删除模型 {name}?',
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
import type { FormItemRule } from 'element-plus';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
export const NAME_PATTERN = /^[A-Za-z0-9\u4e00-\u9fa5][A-Za-z0-9\u4e00-\u9fa5-_]*$/;
|
||||
/** Backend Pattern: ^[A-Za-z0-9\u4e00-\u9fa5][A-Za-z0-9\u4e00-\u9fa5-_#@/.|]{1,31}$ */
|
||||
export const NAME_PATTERN = /^[A-Za-z0-9\u4e00-\u9fa5][A-Za-z0-9\u4e00-\u9fa5\-_#@/.|]{1,31}$/;
|
||||
/** Backend Pattern: ^[A-Za-z0-9][A-Za-z0-9-_#@/.|]{1,31}$ (no Chinese) */
|
||||
export const AUTH_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9\-_#@/.|]{1,31}$/;
|
||||
/** Backend Pattern: ^1([3-9])\d{9}$ */
|
||||
export const PHONE_PATTERN = /^1([3-9])\d{9}$/;
|
||||
/** Backend Pattern: ^[A-Za-z0-9_.-]+@[A-Za-z0-9]+\.[A-Za-z0-9]+$ */
|
||||
export const EMAIL_PATTERN = /^[A-Za-z0-9_.\-]+@[A-Za-z0-9]+\.[A-Za-z0-9]+$/;
|
||||
export const DECIMAL_PATTERN = /^-?(([0-9]*(\.[0-9]{1,3})$)|([0-9]+$))/;
|
||||
|
||||
export function nameRules(t: ComposerTranslation, entityName: string): FormItemRule[] {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<el-checkbox v-model="form.reasoning">{{ $t('agentic.capReasoning') }}</el-checkbox>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('settings.agentic.temperature')">
|
||||
<el-form-item :label="$t('settings.agentic.temperature')" prop="temperature">
|
||||
<el-slider v-model="form.temperature" :max="2" :min="0" :step="0.1" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('settings.agentic.maxTokens')" prop="maxTokens">
|
||||
@@ -125,6 +125,15 @@
|
||||
model: [{ required: true, message: t('settings.agentic.modelRequired'), trigger: 'blur' }],
|
||||
providerId: [{ required: true, message: t('settings.agentic.providerRequired'), trigger: 'change' }],
|
||||
maxTokens: [{ required: true, message: t('settings.agentic.maxTokensRequired'), trigger: 'blur' }],
|
||||
temperature: [
|
||||
{
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 2,
|
||||
message: t('settings.agentic.temperatureRange'),
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const show = () => {
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
POINT_TYPE_OPTIONS,
|
||||
} from '@/config/constant/enums';
|
||||
import type { CommandForm, CommandParamRecord, CommandRecord } from '@/config/types';
|
||||
import { NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
import { failMessage } from '@/utils/notificationUtil';
|
||||
import {
|
||||
callTypeValue,
|
||||
@@ -210,10 +211,15 @@
|
||||
paramLoading: false,
|
||||
});
|
||||
|
||||
const PARAM_NAME_RE = /^[A-Za-z0-9一-龥][A-Za-z0-9一-龥\-_#@/.|]{1,31}$/;
|
||||
|
||||
const rules: FormRules = {
|
||||
commandName: [{ required: true, message: t('command.form.nameRequired'), trigger: 'blur' }],
|
||||
commandName: [
|
||||
{ required: true, message: t('command.form.nameRequired'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
commandTypeFlag: [{ required: true, message: t('command.form.commandTypeRequired'), trigger: 'change' }],
|
||||
callTypeFlag: [{ required: true, message: t('command.form.callTypeRequired'), trigger: 'change' }],
|
||||
timeout: [{ required: true, message: t('command.form.timeoutRequired'), trigger: 'blur' }],
|
||||
};
|
||||
|
||||
type RowErrors = { paramName?: string; paramCode?: string };
|
||||
@@ -227,7 +233,7 @@
|
||||
const code = String(row.paramCode || '').trim();
|
||||
if (!name) {
|
||||
errors.paramName = t('command.form.paramRequired');
|
||||
} else if (!PARAM_NAME_RE.test(name)) {
|
||||
} else if (!NAME_PATTERN.test(name)) {
|
||||
errors.paramName = t('command.form.paramNamePattern');
|
||||
}
|
||||
if (!code) {
|
||||
@@ -301,7 +307,7 @@
|
||||
failMessage(t('command.form.paramRequired'));
|
||||
valid = false;
|
||||
}
|
||||
if (item.paramName && !PARAM_NAME_RE.test(item.paramName)) {
|
||||
if (item.paramName && !NAME_PATTERN.test(item.paramName)) {
|
||||
failMessage(t('command.form.paramNamePattern'));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { EVENT_LEVEL_OPTIONS, EVENT_TYPE_OPTIONS, POINT_TYPE_OPTIONS } from '@/config/constant/enums';
|
||||
import type { EventForm, EventParamRecord, EventRecord } from '@/config/types';
|
||||
import { NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
import { failMessage } from '@/utils/notificationUtil';
|
||||
import { enableFlagValue, eventLevelValue, eventTypeValue, pointTypeValue } from '@/utils/thingModelFormatUtil';
|
||||
|
||||
@@ -172,10 +173,14 @@
|
||||
paramLoading: false,
|
||||
});
|
||||
|
||||
const PARAM_NAME_RE = /^[A-Za-z0-9一-龥][A-Za-z0-9一-龥\-_#@/.|]{1,31}$/;
|
||||
|
||||
const rules: FormRules = {
|
||||
eventName: [{ required: true, message: t('eventDefinition.form.nameRequired'), trigger: 'blur' }],
|
||||
eventName: [
|
||||
{ required: true, message: t('eventDefinition.form.nameRequired'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
eventTypeFlag: [{ required: true, message: t('eventDefinition.form.eventTypeRequired'), trigger: 'change' }],
|
||||
eventLevelFlag: [{ required: true, message: t('eventDefinition.form.eventLevelRequired'), trigger: 'change' }],
|
||||
};
|
||||
|
||||
type RowErrors = { paramName?: string; paramCode?: string };
|
||||
@@ -189,7 +194,7 @@
|
||||
const code = String(row.paramCode || '').trim();
|
||||
if (!name) {
|
||||
errors.paramName = t('eventDefinition.form.paramRequired');
|
||||
} else if (!PARAM_NAME_RE.test(name)) {
|
||||
} else if (!NAME_PATTERN.test(name)) {
|
||||
errors.paramName = t('eventDefinition.form.paramNamePattern');
|
||||
}
|
||||
if (!code) {
|
||||
@@ -256,7 +261,7 @@
|
||||
failMessage(t('eventDefinition.form.paramRequired'));
|
||||
valid = false;
|
||||
}
|
||||
if (item.paramName && !PARAM_NAME_RE.test(item.paramName)) {
|
||||
if (item.paramName && !NAME_PATTERN.test(item.paramName)) {
|
||||
failMessage(t('eventDefinition.form.paramNamePattern'));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { ENTITY_TYPE_OPTIONS } from '@/config/constant/enums';
|
||||
import type { GroupRecord } from '@/config/types/manager';
|
||||
import { NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -85,7 +86,11 @@ export default defineComponent({
|
||||
|
||||
const rules: FormRules = {
|
||||
groupTypeFlag: [{ required: true, message: t('settings.common.entityTypePlaceholder'), trigger: 'change' }],
|
||||
groupName: [{ required: true, message: t('settings.group.groupNamePlaceholder'), trigger: 'blur' }],
|
||||
groupName: [
|
||||
{ required: true, message: t('settings.group.groupNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
};
|
||||
|
||||
const excludedIds = computed(() => {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { ENTITY_TYPE_OPTIONS } from '@/config/constant/enums';
|
||||
import type { LabelRecord } from '@/config/types/manager';
|
||||
import { NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -53,7 +54,11 @@ export default defineComponent({
|
||||
|
||||
const rules: FormRules = {
|
||||
entityTypeFlag: [{ required: true, message: t('settings.common.entityTypePlaceholder'), trigger: 'change' }],
|
||||
labelName: [{ required: true, message: t('settings.label.labelNamePlaceholder'), trigger: 'blur' }],
|
||||
labelName: [
|
||||
{ required: true, message: t('settings.label.labelNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { MENU_LEVEL_OPTIONS, MENU_TYPE_OPTIONS } from '@/config/constant/enums';
|
||||
import { iconMap, iconNames, resolveIcon } from '@/config/constant/icons';
|
||||
import { AUTH_NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -65,12 +66,17 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
menuName: [{ required: true, message: t('settings.menu.menuNamePlaceholder'), trigger: 'blur' }],
|
||||
menuName: [
|
||||
{ required: true, message: t('settings.menu.menuNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: AUTH_NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
menuCode: [{ required: true, message: t('settings.menu.menuCodePlaceholder'), trigger: 'blur' }],
|
||||
titleZh: [{ required: true, message: t('settings.menu.titleZhPlaceholder'), trigger: 'blur' }],
|
||||
titleEn: [{ required: true, message: t('settings.menu.titleEnPlaceholder'), trigger: 'blur' }],
|
||||
menuTypeFlag: [{ required: true, trigger: 'change' }],
|
||||
menuLevel: [{ required: true, trigger: 'change' }],
|
||||
menuTypeFlag: [{ required: true, message: t('settings.menu.menuTypeRequired'), trigger: 'change' }],
|
||||
menuLevel: [{ required: true, message: t('settings.menu.menuLevelRequired'), trigger: 'change' }],
|
||||
parentMenuId: [{ required: true, message: t('settings.menu.parentMenuIdPlaceholder'), trigger: 'change' }],
|
||||
};
|
||||
|
||||
const parentTreeOptions = computed(() => [{ id: 0, menuName: 'Root', children: props.treeData || [] }]);
|
||||
|
||||
@@ -20,6 +20,7 @@ import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { AUTH_NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -75,7 +76,11 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
resourceName: [{ required: true, message: t('settings.resource.resourceNamePlaceholder'), trigger: 'blur' }],
|
||||
resourceName: [
|
||||
{ required: true, message: t('settings.resource.resourceNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: AUTH_NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
parentResourceId: [
|
||||
{ required: true, message: t('settings.resource.parentResourceIdPlaceholder'), trigger: 'change' },
|
||||
],
|
||||
|
||||
@@ -20,6 +20,7 @@ import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { AUTH_NAME_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -56,7 +57,11 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
roleName: [{ required: true, message: t('settings.role.roleNamePlaceholder'), trigger: 'blur' }],
|
||||
roleName: [
|
||||
{ required: true, message: t('settings.role.roleNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: AUTH_NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
parentRoleId: [{ required: true, message: t('settings.role.parentRoleIdPlaceholder'), trigger: 'change' }],
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
|
||||
import { AUTH_NAME_PATTERN, EMAIL_PATTERN, NAME_PATTERN, PHONE_PATTERN } from '@/utils/formRuleUtil';
|
||||
|
||||
type FormMode = 'add' | 'edit';
|
||||
|
||||
@@ -49,8 +50,18 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
userName: [{ required: true, message: t('settings.user.userNamePlaceholder'), trigger: 'blur' }],
|
||||
nickName: [{ required: true, message: t('settings.user.nickNamePlaceholder'), trigger: 'blur' }],
|
||||
userName: [
|
||||
{ required: true, message: t('settings.user.userNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: AUTH_NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
nickName: [
|
||||
{ required: true, message: t('settings.user.nickNamePlaceholder'), trigger: 'blur' },
|
||||
{ min: 2, max: 32, message: t('common.nameLength'), trigger: 'blur' },
|
||||
{ pattern: NAME_PATTERN, message: t('common.nameFormat'), trigger: 'blur' },
|
||||
],
|
||||
phone: [{ pattern: PHONE_PATTERN, message: t('settings.user.phoneFormat'), trigger: 'blur' }],
|
||||
email: [{ pattern: EMAIL_PATTERN, message: t('settings.user.emailFormat'), trigger: 'blur' }],
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
|
||||
Reference in New Issue
Block a user