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:
1 parent
c83287819a
commit
8355e0cc7d
26 files changed
+2785
-180
No files matched your search
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user