diff --git a/src/config/i18n/locales/en.ts b/src/config/i18n/locales/en.ts index 5b5dd19c1..21aa68a9e 100644 --- a/src/config/i18n/locales/en.ts +++ b/src/config/i18n/locales/en.ts @@ -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}?', diff --git a/src/config/i18n/locales/zh.ts b/src/config/i18n/locales/zh.ts index 1daec7e46..c652783b7 100644 --- a/src/config/i18n/locales/zh.ts +++ b/src/config/i18n/locales/zh.ts @@ -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}?', diff --git a/src/utils/formRuleUtil.ts b/src/utils/formRuleUtil.ts index b9eccae68..dece214ff 100644 --- a/src/utils/formRuleUtil.ts +++ b/src/utils/formRuleUtil.ts @@ -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[] { diff --git a/src/views/settings/agentic/edit/ModelConfigEditForm.vue b/src/views/settings/agentic/edit/ModelConfigEditForm.vue index 5dddced87..432db5b1b 100644 --- a/src/views/settings/agentic/edit/ModelConfigEditForm.vue +++ b/src/views/settings/agentic/edit/ModelConfigEditForm.vue @@ -50,7 +50,7 @@ {{ $t('agentic.capReasoning') }} - + @@ -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 = () => { diff --git a/src/views/settings/command/edit/CommandEditForm.vue b/src/views/settings/command/edit/CommandEditForm.vue index da7617e28..6c30674df 100644 --- a/src/views/settings/command/edit/CommandEditForm.vue +++ b/src/views/settings/command/edit/CommandEditForm.vue @@ -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; } diff --git a/src/views/settings/event/definition/edit/EventEditForm.vue b/src/views/settings/event/definition/edit/EventEditForm.vue index 797b89359..153af43da 100644 --- a/src/views/settings/event/definition/edit/EventEditForm.vue +++ b/src/views/settings/event/definition/edit/EventEditForm.vue @@ -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; } diff --git a/src/views/settings/group/edit/index.ts b/src/views/settings/group/edit/index.ts index a65e5eba2..7d2134e0b 100644 --- a/src/views/settings/group/edit/index.ts +++ b/src/views/settings/group/edit/index.ts @@ -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(() => { diff --git a/src/views/settings/label/edit/index.ts b/src/views/settings/label/edit/index.ts index e6906b21e..3ddb72a7c 100644 --- a/src/views/settings/label/edit/index.ts +++ b/src/views/settings/label/edit/index.ts @@ -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 = () => { diff --git a/src/views/settings/menu/edit/index.ts b/src/views/settings/menu/edit/index.ts index 2202622e5..f6ef431b5 100644 --- a/src/views/settings/menu/edit/index.ts +++ b/src/views/settings/menu/edit/index.ts @@ -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 || [] }]); diff --git a/src/views/settings/resource/edit/index.ts b/src/views/settings/resource/edit/index.ts index 08e9329ed..e116ce5e2 100644 --- a/src/views/settings/resource/edit/index.ts +++ b/src/views/settings/resource/edit/index.ts @@ -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' }, ], diff --git a/src/views/settings/role/edit/index.ts b/src/views/settings/role/edit/index.ts index 62b9173db..a67e0295c 100644 --- a/src/views/settings/role/edit/index.ts +++ b/src/views/settings/role/edit/index.ts @@ -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' }], }; diff --git a/src/views/settings/user/edit/index.ts b/src/views/settings/user/edit/index.ts index 2aa13e4a0..e75e6880a 100644 --- a/src/views/settings/user/edit/index.ts +++ b/src/views/settings/user/edit/index.ts @@ -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 = () => {