feat(client): 初始化前端壳与鉴权守卫(P1-5)
- 技术栈:React 18 + TypeScript + Vite + Tailwind - API 层:ApiError + 401 自动刷新 + single-flight 重试 - 鉴权:内存 Access Token + Cookie Refresh + 启动 bootstrap - RBAC:权限映射 + RequireAuth/RequirePermission 路由守卫 - 布局:OA 工作台壳层 + 侧边栏 + 5 个占位页面 - 样式:复用 hyb-web 视觉 token(单向同步) - 文档:新增 client README,补充 server 联调说明
这个提交包含在:
@@ -0,0 +1,74 @@
|
||||
const EMPLOYEE_BASE_PERMISSIONS = [
|
||||
"expense.create",
|
||||
"expense.view_own",
|
||||
"expense.edit_own",
|
||||
"expense.delete_own",
|
||||
"expense.submit",
|
||||
"expense.withdraw",
|
||||
"invoice.upload",
|
||||
"invoice.view_own",
|
||||
"expense.proxy_manage",
|
||||
] as const;
|
||||
|
||||
const ROLE_PERMISSIONS: Record<string, readonly string[]> = {
|
||||
employee: EMPLOYEE_BASE_PERMISSIONS,
|
||||
manager: [
|
||||
...EMPLOYEE_BASE_PERMISSIONS,
|
||||
"expense.approve",
|
||||
"expense.transfer",
|
||||
"expense.view_all",
|
||||
],
|
||||
ceo: [
|
||||
...EMPLOYEE_BASE_PERMISSIONS,
|
||||
"expense.approve",
|
||||
"expense.transfer",
|
||||
"expense.view_all",
|
||||
],
|
||||
finance: [
|
||||
...EMPLOYEE_BASE_PERMISSIONS,
|
||||
"invoice.manage",
|
||||
"expense.approve",
|
||||
"expense.finance.pay",
|
||||
"expense.view_all",
|
||||
],
|
||||
finance_mgr: [
|
||||
...EMPLOYEE_BASE_PERMISSIONS,
|
||||
"invoice.manage",
|
||||
"expense.approve",
|
||||
"expense.finance.pay",
|
||||
"expense.view_all",
|
||||
"expense.finance.override_duplicate",
|
||||
"system.settings",
|
||||
],
|
||||
administration: ["expense.proxy_submit", "expense.view_own"],
|
||||
hr: ["system.users", "expense.view_all"],
|
||||
admin: ["*"],
|
||||
};
|
||||
|
||||
function rolePermissions(roleCode: string): readonly string[] {
|
||||
return ROLE_PERMISSIONS[roleCode] ?? [];
|
||||
}
|
||||
|
||||
export function resolvePermissionsForRoles(roleCodes: string[]) {
|
||||
const permissions = new Set<string>();
|
||||
|
||||
for (const roleCode of roleCodes) {
|
||||
for (const permission of rolePermissions(roleCode)) {
|
||||
permissions.add(permission);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(permissions);
|
||||
}
|
||||
|
||||
export function hasPermission(permissionCodes: string[], permissionCode: string) {
|
||||
return permissionCodes.includes("*") || permissionCodes.includes(permissionCode);
|
||||
}
|
||||
|
||||
export function hasAnyPermission(permissionCodes: string[], candidates: string[]) {
|
||||
if (candidates.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return candidates.some((candidate) => hasPermission(permissionCodes, candidate));
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户