feat(client): 首次登录改密守卫与错误文案(F35,F38)

- 登录响应支持 mustChangePassword 并强制跳转改密页
- 新增改密页面与全局路由守卫
- 完善 AUTH_002/SYS_429/VALID_00x 等错误提示
这个提交包含在:
2026-02-09 18:16:22 +08:00
父节点 7d50a85412
当前提交 2566289d37
共修改 8 个文件,包含 265 行新增和 13 行删除
+51
查看文件
@@ -7,6 +7,17 @@ export function mapApiErrorMessage(
return "账号或密码错误,请检查后重试。";
}
if (code === "AUTH_002") {
const lockedUntil = typeof details?.lockedUntil === "string" ? details.lockedUntil : null;
if (lockedUntil) {
const until = new Date(lockedUntil);
if (!Number.isNaN(until.getTime())) {
return `账号已锁定至 ${until.toLocaleString()},请稍后重试。`;
}
}
return "账号已锁定,请稍后重试。";
}
if (code === "AUTH_003") {
return "账号已离职/禁用,请联系管理员。";
}
@@ -15,6 +26,10 @@ export function mapApiErrorMessage(
return "登录状态失效,请重新登录。";
}
if (code === "AUTH_005") {
return "登录状态已失效,请重新登录。";
}
if (code === "PERM_001") {
return "当前账号无权限执行该操作。";
}
@@ -31,10 +46,34 @@ export function mapApiErrorMessage(
return "服务暂不可用,请稍后重试或联系管理员。";
}
if (code === "SYS_429") {
return "请求过于频繁,请稍后重试。";
}
if (code === "VALID_002") {
return "输入内容长度不足,请补充后再提交。";
}
if (code === "VALID_003") {
return "票据疑似重复,请核对后再上传或联系财务处理。";
}
if (code === "VALID_004") {
return "原始单据已审批通过/付款,当前操作不允许。";
}
if (code === "VALID_005") {
return "票据已绑定报销单,无需重复绑定。";
}
if (code === "VALID_006") {
return "OCR 重试次数已达上限,请稍后人工处理。";
}
if (code === "VALID_007") {
return "OCR 重试过于频繁,请稍后再试。";
}
if (code === "VALID_001") {
if (details?.payment === "already_paid_conflict") {
return "该报销单已支付且付款信息冲突,请刷新后确认。";
@@ -63,6 +102,18 @@ export function mapApiErrorMessage(
if (details?.task === "already_processed") {
return "该任务已被处理,请刷新列表。";
}
if (details?.invoice === "not_bound") {
return "该票据未绑定报销单,无法解绑。";
}
if (details?.reimbursement === "invalid_status_for_unbind") {
return "仅草稿/驳回状态的报销单允许解绑票据。";
}
if (details?.ocr === "disabled") {
return "当前环境未开启 OCR,请联系管理员或切换到 mock/tencent。";
}
}
return fallbackMessage || "请求失败,请稍后重试。";