diff --git a/frontend/platform_admin/src/api/http.ts b/frontend/platform_admin/src/api/http.ts index 916f614..9d68dc8 100644 --- a/frontend/platform_admin/src/api/http.ts +++ b/frontend/platform_admin/src/api/http.ts @@ -5,6 +5,17 @@ export const tokenStorageKey = 'token'; export type PageResult = { total: number; list: T[] }; +// 将后端 SDK 的通用英文错误转换为面向用户的中文提示。 +const API_ERROR_MESSAGES: Record = { + 'Invalid Argument': '请求参数不正确,请检查填写内容', +}; + +// 优先保留后端提供的具体中文信息,仅翻译已知的通用英文错误。 +function localizeApiErrorMessage(message?: string) { + if (!message) return '请求失败'; + return API_ERROR_MESSAGES[message.trim()] ?? message; +} + export async function request(path: string, init?: RequestInit): Promise { const token = localStorage.getItem(tokenStorageKey); let response: Response; @@ -21,6 +32,8 @@ export async function request(path: string, init?: RequestInit): Promise { throw new Error('无法连接服务器,请确认服务已启动'); } const payload = (await response.json()) as { code?: number; message?: string; details?: T }; - if (!response.ok || payload.code !== 0) throw new Error(payload.message || '请求失败'); + if (!response.ok || payload.code !== 0) { + throw new Error(localizeApiErrorMessage(payload.message)); + } return payload.details as T; } diff --git a/frontend/platform_admin/src/views/shared/CrudListPage.vue b/frontend/platform_admin/src/views/shared/CrudListPage.vue index 659b562..0721ba7 100644 --- a/frontend/platform_admin/src/views/shared/CrudListPage.vue +++ b/frontend/platform_admin/src/views/shared/CrudListPage.vue @@ -241,6 +241,7 @@ {{ optionLabel(option) }} + @@ -310,6 +311,22 @@ const roleOptions = ref([]); const relationOptions = reactive>({}); const relationLoading = reactive>({}); const relationSearchTimers = new Map>(); +// 账户密码规则与后端 AccountPasswordMinLength 保持一致。 +const ACCOUNT_PASSWORD_MIN_LENGTH = 6; +const ACCOUNT_PASSWORD_LENGTH_MESSAGE = '密码长度不能少于6个字符'; + +// 检查当前表单中的密码字段,按 Unicode 字符数与后端规则对齐。 +function hasInvalidPassword( + fields: ResourceField[], + values: Record, +) { + return fields.some( + (field) => + field.type === 'password' && + !isMissingField(values[field.key]) && + Array.from(String(values[field.key])).length < ACCOUNT_PASSWORD_MIN_LENGTH, + ); +} const managedOwnerIdentity = computed(() => typeof route.query.owner_identity === 'string' ? route.query.owner_identity : '', ); @@ -683,6 +700,13 @@ async function saveAccount() { Message.warning('请填写用户名和密码'); return; } + if ( + !accountEditingIdentity.value && + Array.from(String(accountForm.password ?? '')).length < ACCOUNT_PASSWORD_MIN_LENGTH + ) { + Message.warning(ACCOUNT_PASSWORD_LENGTH_MESSAGE); + return; + } accountSaving.value = true; try { const payload: Record = { @@ -873,6 +897,10 @@ async function submitDetailAction() { Message.warning('请填写必填字段'); return; } + if (hasInvalidPassword(action.fields ?? [], actionForm)) { + Message.warning(ACCOUNT_PASSWORD_LENGTH_MESSAGE); + return; + } await executeDetailAction(action); } @@ -912,6 +940,10 @@ async function save() { Message.warning('请填写必填字段'); return; } + if (hasInvalidPassword(formFields.value, form)) { + Message.warning(ACCOUNT_PASSWORD_LENGTH_MESSAGE); + return; + } saving.value = true; try { const payload = buildResourcePayload(