feat(system): 完成前端系统管理(布局框架、三个管理页面、e2e)

- BasicLayout:侧边栏/顶栏/面包屑/移动端抽屉;菜单从路由表派生;
  路由守卫新增 requiresAdmin 层(菜单隐藏 + 直连 URL 弹回)
- 用户/角色/日志三个页面:分页请求序号防竞态、确认动作流抽 useConfirmAction、
  时间范围配 default-time、日志详情按需懒加载并缓存
- 空 catch 统一改 handledError 显式声明;词表/选项常量集中(与后端对齐),
  「停用」文案全站统一为「禁用」
- 品牌色提升为全局 token,Element Plus 主色指向赭金
- e2e:公共工具抽 common.py;系统管理 13 项(含详情懒加载脱敏断言),
  管理员密码改 E2E_ADMIN_PASSWORD 环境变量;login_flow 8 项回归通过

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-10 16:28:53 +08:00
1 parent c83287819a
commit 8355e0cc7d
26 files changed
+2785 -180

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
/** 表单校验工具。 */
/**
* 密码校验:按 **UTF-8 字节数** 而非字符数。
*
* 后端 bcrypt 对超过 72 字节的密码直接拒绝;中文一个字符占 3 字节,
* 24 个汉字即到顶 —— 按字符数校验(如 maxlength=20)根本拦不住,
* 会出现"前端放行、后端 400"的错位体验。与后端 `PASSWORD_*_BYTES` 对齐。
*/
export const PASSWORD_MIN_BYTES = 8
export const PASSWORD_MAX_BYTES = 72
const BYTE_HINT = '(中文一个字符约 3 字节)'
/** 计算 UTF-8 字节数 */
export function passwordByteLength(value: string): number {
return new TextEncoder().encode(value).length
}
/** Element Plus 表单校验器(用于 el-form 的 rules.validator) */
export function passwordRule(
_rule: unknown,
value: string,
callback: (error?: Error) => void,
): void {
if (!value) {
callback(new Error('请输入密码'))
return
}
const length = passwordByteLength(value)
if (length < PASSWORD_MIN_BYTES) {
callback(new Error(`密码不足 ${PASSWORD_MIN_BYTES} 字节${BYTE_HINT}`))
} else if (length > PASSWORD_MAX_BYTES) {
callback(new Error(`密码超过 ${PASSWORD_MAX_BYTES} 字节上限${BYTE_HINT}`))
} else {
callback()
}
}