SAAS雲平台搭建札記: (三) AntDesign + .Net Core WebAPI權限控制、動態菜單的生成

我們知道,當下最火的前端框架,非螞蟻金服的AntDesign莫屬,這個框架不僅在國內非常有名,在國外GitHub上React前端框架也排名第一。而且這個框架涵蓋了React、Vue、Angular等多種語言,甚至有人結合.net Core 5的新特性WebAssembly做了Ant Design Blazor,在此為國人點贊!

公司的新平台,用戶前端界面當仁不讓地使用了AntDesign for React,可以使用最新版本的特性(目前版本為4.10.1);至於為什麼不使用Ant Design Pro,是因為Pro封裝的控件太多,不利於我們自定義頁面。

SAAS系統,頁面上首先就是權限,我們後台採用中等複雜度的RBAC控制,如圖所示:

在界面上表示,就是程序左側的樹狀菜單,參照AntdPro的官方文檔,路由和菜單,需要在菜單的ts文檔中寫清楚各種權限組和相應菜單,顯然不符合我們前後端分離使用動態菜單的方法。

因此,我研究一段時間,終於找到完全在後端生成動態菜單並且在前端的使用方法,特此分享給大家。

傳遞到前端的菜單實體類:

1     public class 菜單實體類
2     {
3         public string key { get; set; }
4         public string icon { get; set; }
5         public string title { get; set; }
6         public string link { get; set; }
7         public IEnumerable<PortalMenu> children { get; set; }
8     }

實際上是一個遞歸結構的json字符串:

  1 {
  2     "returnCode": 0,
  3     "errorMsg": null,
  4     "data": {
  5         "portalMenus": [{
  6             "key": "R0HGQWqTzE9gzg",
  7             "icon": "DashboardOutlined",
  8             "title": "查詢",
  9             "link": "/Wuire",
 10             "children": []
 11         }, {
 12             "key": "g9asSJsw9yx6w",
 13             "icon": "HomeOutlined",
 14             "title": "管理",
 15             "children": [{
 16                 "key": "GBvD0rfpsYa6w",
 17                 "title": "設定",
 18                 "link": "/Willage",
 19                 "children": []
 20             }, {
 21                 "key": "L3LD2SrK84g",
 22                 "title": "管理",
 23                 "link": "/Wuse",
 24                 "children": []
 25             }, {
 26                 "key": "Wdvue6w",
 27                 "title": "管理",
 28                 "link": "/Wner",
 29                 "children": []
 30             }]
 31         }, {
 32             "key": "R3JvXJWQk6d6A",
 33             "icon": "ContactsOutlined",
 34             "title": "",
 35             "children": [{
 36                 "key": "IIJCXkQfPyzg",
 37                 "title": "群發",
 38                 "children": [{
 39                     "key": "hnhrfYWq29w",
 40                     "title": "郵件",
 41                     "link": "/Wend",
 42                     "children": []
 43                 }, {
 44                     "key": "gF7a1XnHQ",
 45                     "title": "群板",
 46                     "link": "/Wdule",
 47                     "children": []
 48                 }, {
 49                     "key": "a8yaA-u6PNQ",
 50                     "title": "歷史",
 51                     "link": "/Wtory",
 52                     "children": []
 53                 }]
 54             }, {
 55                 "key": "CI03foxpw",
 56                 "title": "群發",
 57                 "children": [{
 58                     "key": "giaPpeiEoY1Rg",
 59                     "title": "短訊",
 60                     "link": "/Wend",
 61                     "children": []
 62                 }, {
 63                     "key": "ewpJBHTcZLjutGQ",
 64                     "title": "模板",
 65                     "link": "/Wuodule",
 66                     "children": []
 67                 }, {
 68                     "key": "0B3qVuvVXpA",
 69                     "title": "歷史",
 70                     "link": "/Wtory",
 71                     "children": []
 72                 }]
 73             }, {
 74                 "key": "7foEYA",
 75                 "title": "信印",
 76                 "link": "/Wurint",
 77                 "children": []
 78             }]
 79         }, {
 80             "key": "f3l981rYVQ",
 81             "icon": "PayCircleOutlined",
 82             "title": "費",
 83             "children": [{
 84                 "key": "DIw69fx0d3Q",
 85                 "title": "每",
 86                 "link": "/Wufei",
 87                 "children": []
 88             }, {
 89                 "key": "PBLCWp73mUV8kA",
 90                 "title": "收定",
 91                 "link": "/WMonth",
 92                 "children": []
 93             }, {
 94                 "key": "jT8bbGMc5EVIw",
 95                 "title": "定",
 96                 "link": "/Wting/ShowfeiXiangmu",
 97                 "children": []
 98             }, {
 99                 "key": "eUsfeeeOzbw",
100                 "title": "表",
101                 "link": "/Wufei/Daily",
102                 "children": []
103             }]
104         }, {
105             "key": "RsLTvHziej3eeg",
106             "icon": "ToolOutlined",
107             "title": "理",
108             "children": [{
109                 "key": "jTqs3ne_FJSxqg",
110                 "title": "報",
111                 "link": "/WuAdd",
112                 "children": []
113             }, {
114                 "key": "GTJetl8mFEQ",
115                 "title": "饋",
116                 "link": "/Wudback",
117                 "children": []
118             }, {
119                 "key": "MFtdebYGvg",
120                 "title": "詢",
121                 "link": "/Wuyu/Inquire",
122                 "children": []
123             }]
124         }, {
125             "key": "OTzJmw",
126             "icon": "MailOutlined",
127             "title": "理",
128             "children": [{
129                 "key": "5x9__uzbmQ",
130                 "title": "發息",
131                 "link": "/Managend",
132                 "children": []
133             }, {
134                 "key": "D6dGz0J-u98iGXw",
135                 "title": "盒",
136                 "link": "/Manage/Inbox",
137                 "children": []
138             }, {
139                 "key": "xNE-jOp4khOHQ",
140                 "title": "群發",
141                 "link": "/ManagpSend",
142                 "children": []
143             }, {
144                 "key": "DbIxzw6Q",
145                 "title": "群發",
146                 "link": "/ManaSend",
147                 "children": []
148             }, {
149                 "key": "JRO7RUL54zaQ",
150                 "title": "群發",
151                 "link": "/ManaoupSend",
152                 "children": []
153             }]
154         }, {
155             "key": "rKYgJZdxqQ",
156             "icon": "TeamOutlined",
157             "title": "用理",
158             "children": [{
159                 "key": "VpTCpsvOsFyUZQ",
160                 "icon": "UserOutlined",
161                 "title": "管理",
162                 "link": "/Mar/List",
163                 "children": []
164             }, {
165                 "key": "YVaswUMx3g",
166                 "icon": "ClusterOutlined",
167                 "title": "部管理",
168                 "link": "/Manist",
169                 "children": []
170             }, {
171                 "key": "nYIdFQ9K0fiNiw",
172                 "icon": "TeamOutlined",
173                 "title": "用管理",
174                 "link": "/MapList",
175                 "children": []
176             }, {
177                 "key": "5cFzOGcLIQ",
178                 "icon": "KeyOutlined",
179                 "title": "用管理",
180                 "link": "/Manage/UsAuthority",
181                 "children": []
182             }]
183         }, {
184             "key": "ab6MCJ9hNUOIfC5ofROgOw",
185             "icon": "SettingOutlined",
186             "title": "系統設置",
187             "children": [{
188                 "key": "PUGYrEbEZ6Q",
189                 "title": "基本設置",
190                 "link": "/Manaasic",
191                 "children": []
192             }, {
193                 "key": "ueve6vGuOGKD8w",
194                 "title": "域名設置",
195                 "link": "/Manas/Domain",
196                 "children": []
197             }]
198         }, {
199             "key": "46lZGOCDyk6saVYzZwdsJA",
200             "icon": "FileTextOutlined",
201             "title": "日誌管理",
202             "children": [{
203                 "key": "ZPi2io3l_EGATyr-9KFk2A",
204                 "title": "系統日誌",
205                 "link": "/Manage/Log/Sys",
206                 "children": []
207             }, {
208                 "key": "Ze8mGMsbmkKTXtPQ",
209                 "title": "操作日誌",
210                 "link": "/Manage/Log/Operate",
211                 "children": []
212             }]
213         }],
214         "defaultMenuId": "RTzE9gzg"
215     }
216 }

 

前端頁面接收後,處理下一二三級菜單,加上圖標,就可以渲染出來了:

 1 ......
 2 
 3   state = {
 4     collapsed: false,
 5     openKeys: [],
 6     menus: null,
 7     defaultMenuId: null,
 8   };
 9 
10   async componentDidMount() {
11     var menus = await getUserMenus();
12     var allMenus = await this.getSubMenus(menus.portalMenus);
13     this.setState({ menus: allMenus, defaultMenuId: menus.defaultMenuId });
14   }
15 
16   getSubMenus = (children) =>{
17     let menuInfo = [];
18     children.forEach(ele=>{
19       if (ele.children && ele.children.length > 0) {
20         menuInfo.push(<SubMenu key={ele.key} title={ele.title} icon={GetIconByName(ele.icon)}>{this.getSubMenus(ele.children)}</SubMenu>);
21       } else {
22         menuInfo.push(<Menu.Item key={ele.key} icon={GetIconByName(ele.icon)}><Link to={ele.link}>{ele.title}</Link></Menu.Item>);
23       }
24     });
25     return menuInfo;
26   };
27 
28   render() {
29     return (
30       <Router>
31         <Layout>
32           <Sider trigger={null} collapsible collapsed={this.state.collapsed}>
33             <div className="logo">
34               .
35             </div>
36             <Menu theme="dark" mode="inline"
37               defaultSelectedKeys = {[this.state.defaultMenuId]}
38               openKeys={this.state.openKeys}
39               onOpenChange={this.onOpenChange}>
40               {this.state.menus}
41             </Menu>
42           </Sider>
43 ......

至此,左邊的菜單就按照每個人的不同權限渲染出來了。

附:前端的getUserMenus和Comm方法:

 1 //用戶取菜單
 2 async function getUserMenus() {
 3     var result = await Comm(....);    
 4     return result.data;
 5 }
 6 
 7 async function Comm(code, ...){
 8   var body = {};
 9   body.Code = code;
10   body.data = ...;
11 
12   var cookie = getCookie(global.......);
13   var headers = {};
14   headers["Content-Type"] = 'application/json';
15   if(cookie){
16     headers.token = cookie;
17   }
18   
19   const response = await fetch(global.webApiUrl,{
20       method: 'POST', 
21       body: JSON.stringify(body), 
22       headers: new Headers(headers)
23     });
24   const rep = await response.json();
25   return rep;
26 }

 

    SAAS雲平台搭建札記系列文章:

    SAAS雲平台搭建札記: (一)淺論SAAS多租戶自助雲服務平台的產品、服務和訂單

    SAAS雲平台搭建札記: (二)Linux Unbutu下.Net Core整套運行環境的搭建

    SAAS雲平台搭建札記: (三) AntDesign + .Net Core WebAPI權限控制、動態菜單的生成