【前言】
找了很多Admin模板,最后還是看中了AntDesignPro(下文簡寫antd pro)這個阿里巴巴開源的Admin框架,長這樣(還行吧,目前挺主流的):
官網地址:https://pro.ant.design/index-cn
該套模板是使用了React開發框架作為基礎,AntDesign(螞蟻金服開源UI組件庫)作為UI庫,集成了Dva,Umi,Mock等庫,如果不是很了解這些內容,門檻還是比較高的。
我們今天需要實現一個需求:將Antd Pro模板中固定寫死的菜單換成從服務器請求菜單然后進行渲染
為啥非要自己寫,官網文檔不香嗎?----官方文檔寫的實在太草了,而且目前沒有typescript方式的文檔,需要踩多少坑...不過沒關系,看完這篇坑就都基本填平了
關於Antd Pro第一步,如何創建並運行Antd Pro,可以移步 https://www.cnblogs.com/7tiny/p/13444963.html
【了解項目中的dva和Umi用法】
antd pro 默認采用了Umi和dva組件,因此想要在基礎上添加功能必須了解數據請求和數據流向
在dva中主要分3層,models,services,components,其中models是最重要概念,這里放的是各種數據,與數據交互的應該都是在這里。services是請求后台接口的方法。components是我們編寫的組件。
service層
service 層僅僅是封裝了用request請求服務端的api的邏輯,這里不涉及業務邏輯,比較簡單,例如下面代碼:
import request from '@/utils/request';
export interface LoginParamsType {
userName: string;
password: string;
mobile: string;
captcha: string;
}
export function fakeAccountLogin(params: LoginParamsType) {
return request('/api/login/account', {
method: 'POST',
data: params,
});
}
export async function getFakeCaptcha(mobile: string) {
return request(`/api/login/captcha?mobile=${mobile}`);
}
models層
models層是核心業務邏輯的所在
import { Effect, Reducer } from 'umi';
import { queryCurrent, query as queryUsers } from '@/services/user';
export interface CurrentUser {
avatar?: string;
name?: string;
title?: string;
group?: string;
signature?: string;
tags?: {
key: string;
label: string;
}[];
userid?: string;
unreadCount?: number;
}
export interface UserModelState {
currentUser?: CurrentUser;
}
//定義接口
export interface UserModelType {
namespace: 'user';
state: UserModelState;
effects: {
fetch: Effect;
};
reducers: {
saveCurrentUser: Reducer<UserModelState>;
};
}
const UserModel: UserModelType = {
//命名空間,component層調用路由方法的時候需要根據命名空間匹配
namespace: 'user',
//存放值的地方
state: {
currentUser: {},
},
// 與后台交互,處理數據邏輯的地方
effects: {
//業務方法,這里的_是不需要參數,如果需要參數,這里改成傳遞進來的參數對象即可
//這里支持call,put,select等幾種方法,用call調用service層的方法
//const m = yield select((state) => state.test.num) //select就是用來選擇上面state里的,這里沒用上
*fetch(_, { call, put }) {
//queryUsers是引入service層那個function的一個名字
const response = yield call(queryUsers);
yield put({
// 這就是reducer中save方法, put就是用來觸發reducer的方法,payload里就是傳過去的參數。 同時它也能觸發同等級effects中其他方法。
type: 'saveCurrentUser',
payload: response,
});
},
},
// 能改變界面的action應該放這里,這里按官方意思不應該做數據處理,只是用來return state 從而改變界面
reducers: {
//可以理解為一個方法
saveCurrentUser(state, action) {
//return新的state,這樣頁面就會更新 es6語法,就是把state全部展開,然后把currentUser重新賦值
return {
...state,
currentUser: action.payload || {},
};
},
},
};
export default UserModel;
component組件中調用model層
在某一個方法里用dispatch即可調用
clickHandler = () => {
dispatch({
type: "user/fetch",// 這里就會觸發models層里面effects中fetch方法(也可以直接觸發reducer中方法,看具體情況) ,user是命名空間名字
payload: {
},
})
}
這里注意一下type請求的方法路由即可,前面是命名空間,后面是方法名
使用connect獲取dispatch調用后改變的state中的數據
首先需要在組件接口中定義字段,類型為model中定義的類型,因為要獲取model中字段這個值,肯定是同一種類型的
然后在組件的實現中定義變量,從props中獲取值
在組件代碼下方的connect方法中將變量賦值,只有這里賦值了,組件中的常量才可以從props中獲取到state中的值
這里connect方法的參數其實就是各個model中的state,dispatch后,這里便可以獲取到修改后的值
tips:因為使用的是typescript,所以這里的參數需要在ConnectState中先定義:
【開始改造Antd Pro,實現從服務器請求菜單】
定義service
在src/services文件夾下新增文件 menu.ts
import request from '@/utils/request';
export async function query(): Promise<any> {
return request('/api/getmenu'); //這里是服務端的獲取菜單的地址,根據自己情況進行調整
}
定義model
在src/models文件夾下新增文件 menu.ts
import { Effect, Reducer } from 'umi';
import { MenuDataItem } from '@ant-design/pro-layout';
import { query } from '@/services/menu';
export interface MenuModelState {
menuData: MenuDataItem[];
}
export interface MenuModelType {
namespace: 'menu';
state: MenuModelState;
effects: {
getMenuData: Effect;
};
reducers: {
saveMenuData: Reducer<MenuModelState>;
};
}
//這里做了個轉換,可能服務端返回的接口格式和前端的路由格式並不一致,可以在這個方法里進行調整,這里的方法僅作參考,根據自己實際情況進行調整即可
const menuFormatter = (response: any) => {
if (response === null)
return [];
var re = response.map((item: { name: string; route: string; children: any; }) => {
const result = {
children: {},
name: item.name,
path: item.route === null ? '/' : item.route,
};
if (item.children) {
result.children = menuFormatter(item.children);
}
return result;
})
return re;
}
const MenuModel: MenuModelType = {
namespace: 'menu',
state: {
menuData: [],
},
effects: {
*getMenuData(_, { put, call }) {
const response = yield call(query);
yield put({
type: 'saveMenuData',
payload: menuFormatter(response.data.viewMenu),
});
},
},
reducers: {
saveMenuData(state, action) {
return {
...state,
menuData: action.payload || [],
};
},
},
};
export default MenuModel;
調整組件 BasicLayout.tsx
antd pro的菜單調整在 src/layouts/BasicLayout.tsx 文件中
這里menuDataRender 默認指向一個方法,我這里不要原來的方式,我們要調整成自己的數據
以下代碼如無特殊說明,都在 BasicLayout.tsx 文件中
1. 調整接口
在 BasicLayoutProps 接口中添加承載菜單的字段
2. 調整組件傳入的字段,useEfect方法中加入請求服務的路由
3. 調整組件menuDataRender為使用傳進來的菜單數據(以前那個方法不用了)
4. 調整connect方法,獲取數據
menu是我們在models/menu.tx中定義的MenuModelState類型,光這里寫會提示錯誤,我們需要在ConnectState中先將該字段定義才可以正常使用
【運行一下試試效果】
執行命令運行一下看看效果
cnpm start
可以看到,已經自動觸發了后端接口,並獲取到了數據,最后又用服務端的數據渲染了菜單
其他內容可以參考官方文檔
antd pro官網地址:https://pro.ant.design/index-cn