From 7dce63dc0b86a3a792c65d3488d5b395ac1afffa Mon Sep 17 00:00:00 2001 From: zhaoying Date: Fri, 27 Mar 2026 17:27:14 +0800 Subject: [PATCH] feat(web): user permissions --- web/src/components/SiderMenu/index.tsx | 32 ++++++++++++++++++++++---- web/src/store/menu.json | 2 +- web/src/views/UserManagement/types.ts | 3 ++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/web/src/components/SiderMenu/index.tsx b/web/src/components/SiderMenu/index.tsx index 4be749e7..d1a4e405 100644 --- a/web/src/components/SiderMenu/index.tsx +++ b/web/src/components/SiderMenu/index.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-02 15:25:31 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-25 12:32:58 + * @Last Modified time: 2026-03-27 16:38:57 */ /** * SiderMenu Component @@ -128,11 +128,35 @@ const Menu: FC<{ /** Filter menus based on user role and source */ useEffect(() => { + let menuList: MenuItem[] = [] + if (user.role === 'member' && source === 'space') { - setMenus((allMenus[source] || []).filter(menu => menu.code !== 'member')) + menuList = (allMenus[source] || []).filter(menu => menu.code !== 'member') } else if (user) { - setMenus(allMenus[source] || []) + menuList = allMenus[source] || [] } + + const noAuthList = ['user', 'pricing'].filter(vo => !user.permissions.includes(vo) && !user.permissions?.includes('all')) + + if (noAuthList && !noAuthList?.includes('all')) { + const filterMenus = (list: MenuItem[]): MenuItem[] =>{ + const filterList = list?.filter(menu => !noAuthList?.includes(menu.code as string)) + + const showList: MenuItem[] = [] + filterList?.forEach(menu => { + const filteredSubs = filterMenus(menu.subs || []) + const hadSubs = menu.subs && menu.subs.length > 0 + if (hadSubs && filteredSubs.length === 0) return + if (menu.type === 'group' && (!menu.subs || menu.subs?.length < 1)) return + showList.push({ ...menu, subs: filteredSubs }) + }) + + return showList + } + menuList = filterMenus(menuList) + } + + setMenus(menuList) }, [source, allMenus, user]) /** Handle menu item click and navigate to path */ @@ -153,7 +177,7 @@ const Menu: FC<{ const iconKey = selectedKeys.includes(menu.path || '') ? `${menu.code}Active` : menu.code; const iconSrc = iconPathMap[iconKey as keyof typeof iconPathMap]; const subs = (menu.subs || []).filter(sub => sub.display); - + /** Leaf node - menu item without children */ if (!subs || subs.length === 0) { if (menu.path) { diff --git a/web/src/store/menu.json b/web/src/store/menu.json index f1453d1e..8d30dcc4 100644 --- a/web/src/store/menu.json +++ b/web/src/store/menu.json @@ -143,7 +143,7 @@ "code": "systemSettings", "label": "systemSettings", "i18nKey": "menu.systemSettings", - "path": "/", + "path": null, "enable": true, "display": true, "level": 1, diff --git a/web/src/views/UserManagement/types.ts b/web/src/views/UserManagement/types.ts index 0250e925..62c93bde 100644 --- a/web/src/views/UserManagement/types.ts +++ b/web/src/views/UserManagement/types.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 17:50:56 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-02-25 11:44:02 + * @Last Modified time: 2026-03-27 16:03:32 */ /** * User data type @@ -18,6 +18,7 @@ export interface User { current_workspace_id?: string; current_workspace_name?: string; role: 'member' | 'manager' | null; + permissions: string[]; [key: string]: unknown; }