本專案主要基于Elux+Antd構建,包含React版本和Vue版本,旨在提供給大家一個簡單基礎、開箱即用的后臺管理系統通用模版,主要包含運行環境、腳手架、代碼風格、基本Layout、狀態管理、路由管理、增刪改查邏輯、串列、表單等,
為保持工程簡單清爽,方便二次開發,只提供基本版式和通用組件,不集成各種眼花繚亂的組件,需要的時候自己加進去就行了,Antd本身也自帶很多組件,
在線預覽
http://admin-react-antd.eluxjs.com/
Git倉庫
- React版本
- github: https://github.com/hiisea/elux-react-antd-admin
- gitee: https://gitee.com/hiisea/elux-react-antd-admin-fork
- Vue版本
- github: https://github.com/hiisea/elux-vue-antd-admin
- gitee: https://gitee.com/hiisea/elux-vue-antd-admin-fork
安裝方法
- 使用Git命令clone相應的庫:
git clone xxx - 也可以使用Elux提供的命令:
npm create elux@latest 或 yarn create elux
你看得見的UI
-
?? 提供通用的Admin系統Layout(包括注冊、登錄、忘記密碼等),
-
?? 動態獲取Menu選單、輪詢最新訊息等,
-
?? 支持第一次后退溢位,自動回到首頁;再次后退則彈出提示:
您確定要離開本站?防止用戶誤操作,
-
提供<DocumentHead>組件,方便在SinglePage中維護document title、keyword、description等,該組件也可用于SSR,例如:
<DocumentHead title={(id?'修改':'新增')+'用戶'} /> -
?? 提供
配置式查詢表單, 還帶TS型別驗證哦,再也不擔心寫錯欄位名:const formItems: SearchFromItems<ListSearchFormData> = [ {name: 'name', label: '用戶名', formItem: <Input placeholder="請輸入關鍵字" />}, {name: 'nickname', label: '呢稱', formItem: <Input placeholder="請輸入呢稱" />}, {name: 'status', label: '狀態', formItem: <Select placeholder="請選擇用戶狀態" />}, {name: 'role', label: '角色', formItem: <Select placeholder="請選擇用戶狀態" />}, {name: 'email', label: 'Email', formItem: <Input placeholder="請輸入Email" />}, ]; -
?? 提供展開與隱藏高級搜索:展開高級 / 隱藏高級
-
?? 提供跨頁選取、重新搜索后選取、review已選取:跨頁選取
-
?? 提供
配置式批量操作等功能,如:批量操作const batchActions = { actions: [ {key: 'delete', label: '批量洗掉', confirm: true}, {key: 'resolved', label: '批量通過', confirm: true}, {key: 'rejected', label: '批量拒絕', confirm: true}, ], handler: (item: {key: string}, ids: (string | number)[]) => { if (item.key === 'delete') { deleteItems(ids as string[]); } else if (item.key === 'resolved') { alterItems(ids as string[], {status: Status.審核通過}); } else if (item.key === 'rejected') { alterItems(ids as string[], {status: Status.審核拒絕}); } }, }; -
?? 提供
資源選擇器,并封裝成select,可單選、多選、選滿自動提交,如:創建文章時,查詢并選擇責任編輯<FormItem {...fromDecorators.editors}> <MSelect<MemberListSearch> placeholder="請選擇責任編輯" selectorPathname="/admin/member/list/selector" fixedSearch={{role: Role.責任編輯, status: Status.啟用}} limit={[1, 2]} returnArray showSearch ></MSelect> </FormItem> -
?? 提供收藏夾書簽功能,用其代替Page選項卡,操作更靈活,點擊左上角【+收藏】試試...
-
?? 提供頁內重繪功能,點擊右上角【重繪按鈕】試試...
-
?? 虛擬Window
- 路由跳轉時可以在新的虛擬視窗中打開,類似于target='_blank',但是虛擬Window哦,如:新視窗打開 / 本視窗打開
- 視窗中可以再開新視窗,最多可達10級
- 彈窗再彈彈表單驗不好?多層彈窗時自動隱藏下層彈窗,關閉上層彈窗自動恢復下層彈窗,保證每一時刻始終之會出現一層彈窗
- 實作真正意義上的Window(非簡單的Dialog),每個視窗不僅擁有獨立的Dom、狀態管理Store、還自動維護獨立的
歷史記錄堆疊 - 提供視窗工具條:后退、重繪、關閉,如:文章串列 => 點擊標題 => 點擊作者 => 點擊文章數,然后你可以依次回退每一步操作,也可一次性全部關閉,
- 提供視窗最大化、最小化按鈕,如:文章詳情,視窗左上角按鈕;并支持默認最大化,如:創建文章
- 視窗可以通過Url發送,如將
http://admin-react-antd.eluxjs.com/admin/member/item/edit/50?__c=_dialog發送給好友后,其可以通過Url還原視窗, - 實作
keep-alive,keep-alive優點是用戶體驗好,缺點是太占資源(需要快取所有Dom元素還有相關記憶體變數),現在使用虛擬Windw,你想keep-alive就在新視窗中打開,不想keep-alive就在原視窗中打開,關閉視窗就自動銷毀keep-alive
-
?? 基于抽象的增刪改查邏輯:
- 業務邏輯通過類的繼承復用,如果是標準的增刪改查基本上
不用寫代碼,否則可以自己覆寫父類中的某些方法:
export class Model extends BaseResource<MemberResource> { protected api = api; protected defaultListSearch = defaultListSearch; }- UI邏輯通過
Hooks復用, - 將視圖抽象成為2大類:串列(List)和單條(Item),抽取其共性,
- 在此基礎上引入視圖
渲染器(Render)概念,類別名+渲染器=具體某個業務視圖,如:- type=list,render=maintain(串列+維護),如:/admin/member/list/maintain
- type=list,render=index(串列+展示),如:/admin/article/list/index
- type=list,render=selector(串列+選擇),如:/admin/member/list/selector
- type=item,render=detail(單條+展示),如:/admin/member/item/detail/49
- type=item,render=edit(單條+編輯),如:/admin/member/item/edit/49
- 業務邏輯通過類的繼承復用,如果是標準的增刪改查基本上
你看不見的幕后
-
?? 使用微模塊架構,將業務功能封裝成獨立微模塊,想要哪個功能就安裝哪個模塊,是一種粒度更細的微前端
你以前的SRC長這樣??? │ ├─ src │ ├─ api # API介面管理 │ ├─ assets # 靜態資源檔案 │ ├─ components # 全域組件 │ ├─ config # 全域配置項 │ ├─ directives # 全域指令檔案 │ ├─ enums # 專案列舉 │ ├─ hooks # 常用 Hooks │ ├─ language # 語言國際化 │ ├─ layout # 框架布局 │ ├─ routers # 路由管理 │ ├─ store # store │ ├─ styles # 全域樣式 │ ├─ typings # 全域 ts 宣告 │ ├─ utils # 工具庫 │ ├─ views # 專案所有頁面 │ ├─ App.vue # 入口頁面 │ └─ main.ts # 入口檔案快來拯救你的SRC??,
使用微模塊后SRC長這樣!!! │ ├─ src │ ├─ moddules # 各業務微模塊 │ │ ├─ user │ │ ├─ article │ │ ├─ comment │ ├─ Project.vue # 各微模塊聚合配置 │ └─ index.ts # 入口檔案- 微模塊支持同步/異步加載
- 微模塊支持本地目錄、支持發布成NPM包,支持獨立部署(微前端)
- 微模塊支持整體TS型別驗證與提示
-
?? 內置最強狀態管理框架(-):
- 同時支持
React/Vue的狀態管理框架, - 最大程度簡化action和store的寫法
export class Model extends BaseMode { @reducer //類似Vuex的mutations public putCurUser(curUser: CurUser) { this.state.curUser = curUser; // vue中可直接修改 //this.state = {...this.state, curUser} react中 } @effect() //類似Vuex的action public async login(args: LoginParams) { const curUser = await api.login(args); this.dispatch(this.actions.putCurUser(curUser)); this.getRouter().relaunch({url: AdminHomeUrl}, 'window'); } }- 與路由結合,支持Store多實體,
- 路由跳轉時自動清理Store,再也不用擔心State無限累積,
- 為action引入執行緒機制,支持在處理action的程序中,在派生出新的action執行緒,
- action執行中支持異步操作:
@effect() public async updateItem(id: string, data: UpdateItem) { await this.api.updateItem!({id, data}); //呼叫后臺API await this.getRouter().back(1, 'window'); //路由后退一步(到串列頁) message.success('編輯成功!'); //提示 await this.getRouter().back(0, 'page'); //back(0)表示重繪當前頁(串列頁) }- 支持
awiat dispatch(action)執行,如在UI中等待login這個action的執行結果:
const onSubmit = (values: HFormData) => { const result = dispatch(stageActions.login(values)); //stageActions.login()中包含異步請求,回傳Promise result.catch(({message}) => { //如果出錯(密碼錯誤),在form中展示出錯資訊 form.setFields([{name: 'password', errors: [message]}]); }); };- 為action引入事件機制,dispatch一個action支持多處監聽,共同協作完成一個長流程業務,例如:ModelA 和 ModelB 都想監聽
用戶切換這個Action:
// ModelA: export class ModelA extends BaseResource { @effect() public async ['stage.putCurUser'](user: User) { if (user.hasLogin) { this.dispath(this.actions.xxx()); } else { this.dispath(this.actions.xxx()); } } } // ModelB: export class ModelB extends BaseResource{ @effect() public async ['stage.putCurUser'](user: User) { if (user.hasLogin) { this.dispath(this.actions.xxx()); } else { this.dispath(this.actions.xxx()); } } }- 路由跳轉前會自動派發
stage._testRouteChange的action,你可以監聽它,阻止路由跳轉:
@effect(null) protected async ['this._testRouteChange']({url, pathname}) { if (!this.state.curUser.hasLogin && this.checkNeedsLogin(pathname)) { throw new CustomError(CommonErrorCode.unauthorized, '請登錄!'); } }- 支持catch action執行程序中的錯誤,并決定繼續或終止當前action執行:
@effect(null) protected async ['this._error'](error: CustomError) { if (error.code === CommonErrorCode.unauthorized) { this.getRouter().push({url: '/login'}, 'window'); }else{ alert(error.message); } throw error; }-
最方便的注入loading狀態,想要跟蹤異步action的執行情況?只需要在宣告方法中傳人key名就行了,如:
@effect('this.listLoading') //將該異步action的執行狀態注入this.state.listLoading中 public async fetchList(listSearchData?: TDefineResource['ListSearch']) { const listSearch = listSearchData || this.state.listSearch || this.defaultListSearch; const {list, listSummary} = await this.api.getList!(listSearch); this.dispatch(this.privateActions.putList(listSearch, list, listSummary)); } -
武裝到牙齒的Typescript智能提示和自動補全(并且型別自動生成,無需手寫):
- 同時支持
-
?? 提供基于雙堆疊單鏈的虛擬路由,
- 擁有2維歷史記錄堆疊,相當于在
SinglePage中虛擬了一個完整的瀏覽器,頁面可以在原視窗中打開,也可以新開一個虛擬視窗打開,
router.push({url: '/login'}, 'page') //在原視窗中打開 router.push({url: '/login'}, 'window') //在新視窗中打開- 基于虛擬路由,不再直接關聯原生路由,中間可以轉換映射,
- 跨平臺,可用于瀏覽器、服務器SSR、小程式、原生應用,
- 跨框架,可用于React、Vue,
- 不依賴其它路由框架,如react-router、vue-router
- 可完整保存歷史快照,實作
Keepalive,包括Store和Dom元素 - 可訪問和查找歷史記錄,不再只是一個history.length
const length = router.getHistoryLength(); //獲取歷史堆疊中的記錄數 const list = router.getHistory(); //獲取所有歷史記錄 const record = router.findRecordByStep(10); //獲取10步之前的歷史記錄 const record2 = router.findRecordByKey('8_1'); //獲取編號為8_1的歷史記錄例如登錄視窗中點擊“取消登錄”你需要回退到前一個頁面,但此時如果前一個頁面就是需要登錄的頁面,那么登錄視窗又會被重新彈出,所以點擊“取消登錄”應當回退到最近的不需要登錄的頁面:
@effect() public async cancelLogin(): Promise<void> { //在歷史堆疊中找到第一條不需要登錄的記錄 //如果簡單的back(1),前一個頁面需要登錄時會引起回圈 this.getRouter().back((record) => { return !this.checkNeedsLogin(record.location.pathname); }, 'window'); }- 支持路由攔截和路由守衛
@effect(null) protected async ['this._testRouteChange']({url, pathname}) { if (!this.state.curUser.hasLogin && this.checkNeedsLogin(pathname)) { throw new CustomError(CommonErrorCode.unauthorized, '請登錄!'); } }- 支持后退溢位時重定向,比如防止用戶后退過多,不小心退出了本站:
@effect(null) protected async ['this._error'](error: CustomError): Promise<void> { if (error.code === ErrorCodes.ROUTE_BACK_OVERFLOW) {//后退溢位時會拋出 const redirect: string = HomeUrl; //如果已經時主頁,則提示用戶是否退出本站? if (this.getRouter().location.url === redirect && window.confirm('確定要退出本站嗎?')){ //注意: back('')可以退出本站 setTimeout(() => this.getRouter().back(''), 0); } else { //如果不是在主頁,則先回到主頁 setTimeout(() => this.getRouter().relaunch({url: redirect}), 0); } }; }- 可跟蹤和等待路由跳轉完成,例如修改用戶后,需要回傳串列頁面并重繪:
@effect() public async updateItem(id: string, data: UpdateItem) { await this.api.updateItem!({id, data}); await this.getRouter().back(1, 'window'); //可await路由后退 message.success('編輯成功!'); this.getRouter().back(0, 'page'); //back(0)可重繪頁面 }- 提供更多路由跳轉方法
router.push(location, target); //新增 router.replace(location, target); //替換 router.relaunch(location, target); //重置 router.back(stepOrCallback, target) //后退或重繪 - 擁有2維歷史記錄堆疊,相當于在
-
?? 提供與專案同構的本地MockServer,MockServer也使用Typescript,但無需再寫型別檔案,直接從
src/下面與專案共享,支持修改自動重啟, -
?? 開箱即用的腳手架,提供封裝好的
Cli命令列腳手架,不用自己折騰:
-
?? 基本的
eslint/stylelint/babel都已經幫你打包好了,不用安裝各種插件和寫一大堆依賴:"devDependencies": { "@elux/babel-preset": "^1.0.2", "@elux/eslint-plugin": "^1.2.2", "@elux/stylelint-config": "^1.1.1" } -
?? 未完待續...
不使用NPM管理微模塊
專案中的微模塊默認是使用NPM包來管理的,每個微模塊下都有一個package.json檔案,例如:src/modules/admin/package.json,開發時使用了workspace和monorepo來管理:
"workspaces": [
"./src/modules/*"
],
跨微模塊參考時,用的是npm包名,例如:
import {ListSearch} from '@elux-admin-antd/member/entity';
微模塊最大的好處還是在于高內聚,低耦合,不一定要使用NPM方式來管理,如果你不想繞這么一個圈,也可以分分鐘改成普通的單體結構:
//import {ListSearch} from '@elux-admin-antd/member/entity';
import {ListSearch} from '@/modules/member/entity';
只需要在src/tsconfig.json中加入paths別名就可以了:
"paths": {
"@/*": ["./*"]
}
Vue版特別說明
從React版本到Vue版本大概花了2天就完成了,Vue版/React版保持同步,由于Elux踐行“模型驅動”的架構理念,View被刻意寫得很薄,很多邏輯寫在了Model中(因為Model與UI框架無關、Vue和React都可以復用),
所以需要重構的只是View,由于Vue3中可以使用steup+tsx,并且antd-vue與antd-react風格和api基本保持一致,所以能2個版本的差異就更小了,Vue版全程使用tsx撰寫,你也可以自己改成template方式,腳手架已經內置了對.vue檔案的支持,也歡迎有空的小伙伴貢獻原始碼,將其重構為template版,
更多相關文章
- 微模塊-前端業務模塊化探索,拆解巨石應用的又一利器
- 不想當Window的Dialog不是一個好Modal,彈窗翻身記
- 手擼Router,還要啥Router框架?讓react-router/vue-router躺一邊涼快去
- 一種比css_scoped和css_module更優雅的避免css命名沖突小妙招
感謝關注,歡迎參與
開源專案,歡迎參與貢獻原始碼(V)!覺得好用別忘了Github給個Star哦(-_-)...
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/505536.html
標籤:其他
