本项目主要基于Elux+Antd构建,包含React版本和Vue版本,旨在提供给大家一个简单基础开箱即用的后台管理系统通用模版,主要包含运行环境、脚手架、代码风格、基本Layout、状态管理、路由管理、增删改查逻辑、列表、表单等。

为保持工程简单清爽,方便二次开发,只提供基本版式和通用组件,不集成各种眼花缭乱的组件,需要的时候自己加进去就行了,Antd本身也自带很多组件。

在线预览

http://admin-react-antd.eluxjs.com/

Git仓库

安装方法

  • 使用Git命令clone相应的库:git clone xxx
  • 也可以使用Elux提供的命令:npm create elux@latest 或 yarn create elux

你看得见的UI

  • 提供通用的Admin系统Layout(包括注册、登录、忘记密码等)。

  • 动态获取Menu菜单、轮询最新消息等。

  • 支持第一次后退溢出,自动回到首页;再次后退则弹出提示:您确定要离开本站?防止用户误操作。

  • 提供<DocumentHead>组件,方便在SinglePage中维护document title、keyword、description等,该组件也可用于SSR,例如:

    1. <DocumentHead title={(id?'修改':'新增')+'用户'} />
  • 提供配置式查询表单, 还带TS类型验证哦,再也不担心写错字段名:

    1. const formItems: SearchFromItems<ListSearchFormData> = [
    2. {name: 'name', label: '用户名', formItem: <Input placeholder="请输入关键字" />},
    3. {name: 'nickname', label: '呢称', formItem: <Input placeholder="请输入呢称" />},
    4. {name: 'status', label: '状态', formItem: <Select placeholder="请选择用户状态" />},
    5. {name: 'role', label: '角色', formItem: <Select placeholder="请选择用户状态" />},
    6. {name: 'email', label: 'Email', formItem: <Input placeholder="请输入Email" />},
    7. ];
  • 提供展开与隐藏高级搜索:展开高级 / 隐藏高级

  • 提供跨页选取、重新搜索后选取、review已选取:跨页选取

  • 提供配置式批量操作等功能,如:批量操作

    1. const batchActions = {
    2. actions: [
    3. {key: 'delete', label: '批量删除', confirm: true},
    4. {key: 'resolved', label: '批量通过', confirm: true},
    5. {key: 'rejected', label: '批量拒绝', confirm: true},
    6. ],
    7. handler: (item: {key: string}, ids: (string | number)[]) => {
    8. if (item.key === 'delete') {
    9. deleteItems(ids as string[]);
    10. } else if (item.key === 'resolved') {
    11. alterItems(ids as string[], {status: Status.审核通过});
    12. } else if (item.key === 'rejected') {
    13. alterItems(ids as string[], {status: Status.审核拒绝});
    14. }
    15. },
    16. };
  • 提供资源选择器,并封装成select,可单选、多选、选满自动提交,如:创建文章时,查询并选择责任编辑

    1. <FormItem {...fromDecorators.editors}>
    2. <MSelect<MemberListSearch>
    3. placeholder="请选择责任编辑"
    4. selectorPathname="/admin/member/list/selector"
    5. fixedSearch={{role: Role.责任编辑, status: Status.启用}}
    6. limit={[1, 2]}
    7. returnArray
    8. showSearch
    9. ></MSelect>
    10. </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
  • 基于抽象的增删改查逻辑:

    • 业务逻辑通过类的继承复用,如果是标准的增删改查基本上不用写代码,否则可以自己覆盖父类中的某些方法:
    1. export class Model extends BaseResource<MemberResource> {
    2. protected api = api;
    3. protected defaultListSearch = defaultListSearch;
    4. }

你看不见的幕后

  • 使用微模块架构,将业务功能封装成独立微模块,想要哪个功能就安装哪个模块,是一种粒度更细的微前端

    1. 你以前的SRC长这样???

    2. ├─ src
    3. ├─ api # API接口管理
    4. ├─ assets # 静态资源文件
    5. ├─ components # 全局组件
    6. ├─ config # 全局配置项
    7. ├─ directives # 全局指令文件
    8. ├─ enums # 项目枚举
    9. ├─ hooks # 常用 Hooks
    10. ├─ language # 语言国际化
    11. ├─ layout # 框架布局
    12. ├─ routers # 路由管理
    13. ├─ store # store
    14. ├─ styles # 全局样式
    15. ├─ typings # 全局 ts 声明
    16. ├─ utils # 工具库
    17. ├─ views # 项目所有页面
    18. ├─ App.vue # 入口页面
    19. └─ main.ts # 入口文件

    快来拯救你的SRC,

    1. 使用微模块后SRC长这样!!!

    2. ├─ src
    3. ├─ moddules # 各业务微模块
    4. ├─ user
    5. ├─ article
    6. ├─ comment
    7. ├─ Project.vue # 各微模块聚合配置
    8. └─ index.ts # 入口文件
    • 微模块支持同步/异步加载
    • 微模块支持本地目录、支持发布成NPM包,支持独立部署(微前端)
    • 微模块支持整体TS类型验证与提示
  • 内置最强状态管理框架(-):

    • 同时支持React/Vue的状态管理框架。
    • 最大程度简化action和store的写法
    1. export class Model extends BaseMode {
    2. @reducer //类似Vuex的mutations
    3. public putCurUser(curUser: CurUser) {
    4. this.state.curUser = curUser; // vue中可直接修改
    5. //this.state = {...this.state, curUser} react中
    6. }
    7. @effect() //类似Vuex的action
    8. public async login(args: LoginParams) {
    9. const curUser = await api.login(args);
    10. this.dispatch(this.actions.putCurUser(curUser));
    11. this.getRouter().relaunch({url: AdminHomeUrl}, 'window');
    12. }
    13. }
    • 与路由结合,支持Store多实例。
    • 路由跳转时自动清理Store,再也不用担心State无限累积。
    • 为action引入线程机制,支持在处理action的过程中,在派生出新的action线程。
    • action执行中支持异步操作:
    1. @effect()
    2. public async updateItem(id: string, data: UpdateItem) {
    3. await this.api.updateItem!({id, data}); //调用后台API
    4. await this.getRouter().back(1, 'window'); //路由后退一步(到列表页)
    5. message.success('编辑成功!'); //提示
    6. await this.getRouter().back(0, 'page'); //back(0)表示刷新当前页(列表页)
    7. }
    • 支持awiat dispatch(action)执行,如在UI中等待login这个action的执行结果:
    1. const onSubmit = (values: HFormData) => {
    2. const result = dispatch(stageActions.login(values));
    3. //stageActions.login()中包含异步请求,返回Promise
    4. result.catch(({message}) => {
    5. //如果出错(密码错误),在form中展示出错信息
    6. form.setFields([{name: 'password', errors: [message]}]);
    7. });
    8. };
    • 为action引入事件机制,dispatch一个action支持多处监听,共同协作完成一个长流程业务。例如:ModelA 和 ModelB 都想监听用户切换这个Action:
    1. // ModelA:
    2. export class ModelA extends BaseResource {
    3. @effect()
    4. public async ['stage.putCurUser'](user: User) {
    5. if (user.hasLogin) {
    6. this.dispath(this.actions.xxx());
    7. } else {
    8. this.dispath(this.actions.xxx());
    9. }
    10. }
    11. }
    12. // ModelB:
    13. export class ModelB extends BaseResource{
    14. @effect()
    15. public async ['stage.putCurUser'](user: User) {
    16. if (user.hasLogin) {
    17. this.dispath(this.actions.xxx());
    18. } else {
    19. this.dispath(this.actions.xxx());
    20. }
    21. }
    22. }
    • 路由跳转前会自动派发stage._testRouteChange的action,你可以监听它,阻止路由跳转:
    1. @effect(null)
    2. protected async ['this._testRouteChange']({url, pathname}) {
    3. if (!this.state.curUser.hasLogin && this.checkNeedsLogin(pathname)) {
    4. throw new CustomError(CommonErrorCode.unauthorized, '请登录!');
    5. }
    6. }
    • 支持catch action执行过程中的错误,并决定继续或终止当前action执行:
    1. @effect(null)
    2. protected async ['this._error'](error: CustomError) {
    3. if (error.code === CommonErrorCode.unauthorized) {
    4. this.getRouter().push({url: '/login'}, 'window');
    5. }else{
    6. alert(error.message);
    7. }
    8. throw error;
    9. }
    • 最方便的注入loading状态,想要跟踪异步action的执行情况?只需要在声明方法中传人key名就行了,如:

      1. @effect('this.listLoading') //将该异步action的执行状态注入this.state.listLoading中
      2. public async fetchList(listSearchData?: TDefineResource['ListSearch']) {
      3. const listSearch = listSearchData || this.state.listSearch || this.defaultListSearch;
      4. const {list, listSummary} = await this.api.getList!(listSearch);
      5. this.dispatch(this.privateActions.putList(listSearch, list, listSummary));
      6. }
    • 武装到牙齿的Typescript智能提示和自动补全(并且类型自动生成,无需手写):

  • 提供基于双栈单链的虚拟路由。

    • 拥有2维历史记录栈,相当于在SinglePage中虚拟了一个完整的浏览器,页面可以在原窗口中打开,也可以新开一个虚拟窗口打开。
    1. router.push({url: '/login'}, 'page') //在原窗口中打开
    2. router.push({url: '/login'}, 'window') //在新窗口中打开
    • 基于虚拟路由,不再直接关联原生路由,中间可以转换映射。
    • 跨平台,可用于浏览器、服务器SSR、小程序、原生应用。
    • 跨框架,可用于React、Vue,
    • 不依赖其它路由框架,如react-router、vue-router
    • 可完整保存历史快照,实现Keepalive,包括Store和Dom元素
    • 可访问和查找历史记录,不再只是一个history.length
    1. const length = router.getHistoryLength(); //获取历史栈中的记录数
    2. const list = router.getHistory(); //获取所有历史记录
    3. const record = router.findRecordByStep(10); //获取10步之前的历史记录
    4. const record2 = router.findRecordByKey('8_1'); //获取编号为8_1的历史记录

    例如登录窗口中点击“取消登录”你需要回退到前一个页面,但此时如果前一个页面就是需要登录的页面,那么登录窗口又会被重新弹出。所以点击“取消登录”应当回退到最近的不需要登录的页面:

    1. @effect()
    2. public async cancelLogin(): Promise<void> {
    3. //在历史栈中找到第一条不需要登录的记录
    4. //如果简单的back(1),前一个页面需要登录时会引起循环
    5. this.getRouter().back((record) => {
    6. return !this.checkNeedsLogin(record.location.pathname);
    7. }, 'window');
    8. }
    • 支持路由拦截和路由守卫
    1. @effect(null)
    2. protected async ['this._testRouteChange']({url, pathname}) {
    3. if (!this.state.curUser.hasLogin && this.checkNeedsLogin(pathname)) {
    4. throw new CustomError(CommonErrorCode.unauthorized, '请登录!');
    5. }
    6. }
    • 支持后退溢出时重定向,比如防止用户后退过多,不小心退出了本站:
    1. @effect(null)
    2. protected async ['this._error'](error: CustomError): Promise<void> {
    3. if (error.code === ErrorCodes.ROUTE_BACK_OVERFLOW) {//后退溢出时会抛出
    4. const redirect: string = HomeUrl;
    5. //如果已经时主页,则提示用户是否退出本站?
    6. if (this.getRouter().location.url === redirect && window.confirm('确定要退出本站吗?')){
    7. //注意: back('')可以退出本站
    8. setTimeout(() => this.getRouter().back(''), 0);
    9. } else {
    10. //如果不是在主页,则先回到主页
    11. setTimeout(() => this.getRouter().relaunch({url: redirect}), 0);
    12. }
    13. };
    14. }
    • 可跟踪和等待路由跳转完成。例如修改用户后,需要返回列表页面并刷新:
    1. @effect()
    2. public async updateItem(id: string, data: UpdateItem) {
    3. await this.api.updateItem!({id, data});
    4. await this.getRouter().back(1, 'window'); //可await路由后退
    5. message.success('编辑成功!');
    6. this.getRouter().back(0, 'page'); //back(0)可刷新页面
    7. }
    • 提供更多路由跳转方法
    1. router.push(location, target); //新增
    2. router.replace(location, target); //替换
    3. router.relaunch(location, target); //重置
    4. router.back(stepOrCallback, target) //后退或刷新
  • 提供与项目同构的本地MockServer,MockServer也使用Typescript,但无需再写类型文件,直接从src/下面与项目共享,支持修改自动重启。

  • 开箱即用的脚手架。提供封装好的Cli命令行脚手架,不用自己折腾:

  • 基本的eslint/stylelint/babel都已经帮你打包好了,不用安装各种插件和写一大堆依赖:

    1. "devDependencies": {
    2. "@elux/babel-preset": "^1.0.2",
    3. "@elux/eslint-plugin": "^1.2.2",
    4. "@elux/stylelint-config": "^1.1.1"
    5. }
  • 未完待续...

不使用NPM管理微模块

项目中的微模块默认是使用NPM包来管理的,每个微模块下都有一个package.json文件,例如:src/modules/admin/package.json,开发时使用了workspacemonorepo来管理:

  1. "workspaces": [
  2. "./src/modules/*"
  3. ],

微模块引用时,用的是npm包名,例如:

  1. import {ListSearch} from '@elux-admin-antd/member/entity';

微模块最大的好处还是在于高内聚,低耦合,不一定要使用NPM方式来管理。如果你不想绕这么一个圈,也可以分分钟改成普通的单体结构:

  1. //import {ListSearch} from '@elux-admin-antd/member/entity';
  2. import {ListSearch} from '@/modules/member/entity';

只需要在src/tsconfig.json中加入paths别名就可以了:

  1. "paths": {
  2. "@/*": ["./*"]
  3. }

Vue版特别说明

从React版本到Vue版本大概花了2天就完成了,Vue版/React版保持同步,由于Elux践行“模型驱动”的架构理念,View被刻意写得很薄,很多逻辑写在了Model中(因为Model与UI框架无关、Vue和React都可以复用)。

所以需要重构的只是View,由于Vue3中可以使用steup+tsx,并且antd-vueantd-react风格和api基本保持一致,所以能2个版本的差异就更小了。Vue版全程使用tsx编写,你也可以自己改成template方式,脚手架已经内置了对.vue文件的支持。也欢迎有空的小伙伴贡献源码,将其重构为template版

更多相关文章

感谢关注,欢迎参与

开源项目,欢迎参与贡献源码(V)!觉得好用别忘了Github给个Star哦(-_-)...

React版/Vue版都齐了,开源一套【特别】的后台管理系统...的更多相关文章

  1. vue从入门到女装??:从零开始搭建后台管理系统(二)用vue-docute生成线上文档

    教程 vue从入门到女装??:从零开始搭建后台管理系统(一)安装框架 一个系统开发完成了总要有操作说明手册,接口文档之类的东西吧?这种要全部纯手写就很麻烦了,可以借助一些插件,比如: vue-docu ...

  2. vue中组件之间的相互调用,及通用后台管理系统左侧菜单树的迭代生成

    由于本人近期开始学习使用vue搭建一个后端管理系统的前端项目,在左侧生成菜单树的时候遇到了一些问题.在这里记录下 分析:由于本人设定的菜单可以使多级结构,直接使用vue的v-for 遍历并不是很方便. ...

  3. 京东云开发者|关于“React 和 Vue 该用哪个”我真的栓Q

    一.前言:我全都要 面对当今前端界两座大山一样的主流框架,React和Vue,相信很多小伙伴都或多或少都产生过这样疑问,而这样的问题也往往很让人头疼和犹豫不决: 业务场景中是不是团队用什么我就用什么? ...

  4. 技术趋势:React vs Vue vs Angular

    React.Vue 和 Angular 这两年发展状况如何?2019 年哪个技术最值得学习? 前几天 Medium 上有一位作者发表了一篇关于 React.Vue 和 Angular 技术趋势的文章( ...

  5. 使用react全家桶制作博客后台管理系统

    前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基于react全家桶(React.React-r ...

  6. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  7. VSCode--HTML代码片段(基础版,react、vue、jquery)

    起因是最近在学习前端,看的网上的demo也是在react.vue.jquery之间穿插,为了方便一键生成html模板(懒)写demo,有了以下折腾. 本人使用的前端编辑工具是vscode(方便.懒), ...

  8. GO 前后端分离开源后台管理系统 Gfast v2.0.4 版发布

    更新内容:1.适配插件商城,开发环境从后台直接安装插件功能:2.代码生成细节修复及功能完善(支持生成上传文件.图片及富文本编辑器功能):3.增加swagger接口文档生成:4.更新goframe版本至 ...

  9. 2017 年比较 Angular、React、Vue 三剑客(转载)

    为 web 应用选择 JavaScript 开发框架是一件很费脑筋的事.现如今 Angular 和 React 非常流行,并且最近出现的新贵 VueJS 同样博得了很多人的关注.更重要的是,这只是一些 ...

随机推荐

  1. JavaScript中async和await的使用以及队列问题

    宏任务和微任务的队列入门知识,可以参考之前的文章: JavaScript的事件循环机制 宏任务和微任务在前端面试中,被经常提及到,包括口头和笔试题 async && await概念 a ...

  2. Python调用Outlook发邮件

    调用Outlook发送邮件 需安装pypiwin32模块:pip install pypiwin32 1. 发送普通邮件 import win32com.client as win32 outlook ...

  3. 记一次 .NET 某新能源系统 线程疯涨 分析

    一:背景 1. 讲故事 前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决. 等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具 WinDbg. 二: WinD ...

  4. 静态static关键字概述和静态static关键字修饰成员变量

    static关键字 概述 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属 于某个对象的.也就是说,既然属于类,就可以不靠创建对象来调用了 ...

  5. protobuf 的交叉编译使用(C++)

    前言 为了提高通信效率,可以采用 protobuf 替代 XML 和 Json 数据交互格式,protobuf 相对来说数据量小,在进程间通信或者设备之间通信能够提高通信速率.下面介绍 protobu ...

  6. Linux sed工具的使用

    基础知识 - 行编辑工具: 一行一行处理文件内容 - 全屏编辑工具:一次性将文件所有内容加载到内存中 sed编辑器: Stream Editor 工作原理: 逐行处理文件内容,一次读取一行内容到模式空 ...

  7. 关于 Python 的 import

    好久以前就被 Python 的相对与绝对导入所困扰.去年粗浅探究后自以为完全理解,近来又因 sys.path[0] 和 os.getcwd() 的不一致而刷新了认知... Python 官方文档 5. ...

  8. linux服务配置IP或者说可以远程连接

    切换目录 cd /etc/sysconfig/network-scripts ls查看当前目录下的东西 找到ipcfg- 开头的,而且不是iocfg-lo,而上图就是那个ifcfg-ens33. 则进 ...

  9. SQLZOO练习5--join(表的连接)

    game表: id mdate stadium team1 team2 1001 8 June 2012 National Stadium, Warsaw POL GRE 1002 8 June 20 ...

  10. Eolink家族成员回归 — 开源服务Eoapi!

    Eolink 开源产品又回来了!Eoapi 自 2016 年上架 Github 以来,一直备受国内外开发者的欢迎和好评 ,在2018年 Eolink 为了进一步升级该产品而进行了暂时下架.时隔四年,E ...