React全家桶+Material-ui构建的管理系统

2,890 阅读6分钟

前言

各大社区中关于react的后台管理系统已经应有尽有,大部分都是react+antd,偶尔一次看到material-ui这个react UI组件,感觉很特别,于是尝试使用搞一个demo体验一下,经过不断细化转化到目前一个比较完整的项目,特发此文以作记录,希望可以帮助更多的开发者。 本文不太适合react初学者,需要对react有一定基础,大量使用了react-hooks。

简介

一个使用React全家桶(react-router-dom,redux,redux-actions,redux-saga,reselect)+Material-ui构建的后来管理中心。选择Material-ui的理由:1、默认四大颜色主题随意切换,主题可继续扩展,定制;2、内置Grid(栅格)系统,完全兼容mobile,ipad,pc三端浏览器;3、内置icon,可使用icon-font或者直接svg;4、强大的Table(表格组件)内置搜索功能,可直接导出(下载)表格到本地。5、柔和的动画体系,增强用户体验。放个截图-_-

附录

    1. 在线体验:账号:admin密码:123456
    1. 源码地址:https://github.com/SimpleRoom/walker-admin,觉得有用请戳:star~ 会不断更新......
    1. 默认使用: create-react-app
    1. 目前分5个页面:图表数据,个人资料,员工管理,会员管理,线路设计,酒店预订
    1. 难点在于redux、redux-actions、redux-saga、reselect这4者如何串联衔接,来注入管理store。
    1. 目前已经部署上线,后台相关功能会陆续添加更新!线上地址就不公布了~~(😁)

工具概括

  • 1、redux:管理组件state的容器
  • 2、react-redux:React官方控制React组件与Redux的连接容器
  • 3、redux-actions:简化Redux写法的工具
  • 4、redux-saga:Redux处理异步数据的中间件
  • 5、reselect:Redux的选择器工具,精确获取指定state,减少不必要的渲染
  • 6、plop:快速开发工具,自动生成指定模板文件的工具(与react无关,可配置提高开发效率)

功能概况

  • 1、路由权限:登录时接口返回该账号权限级别,匹配对应的路由routerList注入store
  • 2、数据图表:热门景点排名,公司营收图表状况
  • 3、个人资料:个人信息编辑更新
  • 4、员工管理:员工资料增删改查,导出
  • 5、会员管理:每月入会会员统计,即将过期会员统计,会员资料增删改查,导出
  • 6、线路设计:已有线路的编辑更新,删除,添加,可随时更新至对应的app或者小程序
  • 7、酒店预订:根据线路参团来预订合适的酒店

部分代码示例

  • 1、如何串联redux,redux-actions,redux-saga,reselect,如:

//异步获取github开放的个人信息接口,对应目录(src/store/modules/common)
// 1.redux-actions
import { createActions } from 'redux-actions'
export const {
  // 获取github个人信息
  fetchGitInfo,
  setGithubInfo,
} = createActions(
  {
    // 获取github个人信息
    FETCH_GIT_INFO: (username) => ({ username }),
    SET_GITHUB_INFO: (githubData) => ({ githubData}),
  },
)
export default {}

//2.redux-saga
import axios from 'axios'
import { fork, put, takeEvery } from 'redux-saga/effects'
import {
  // github 个人信息
  fetchGitInfo,
  setGithubInfo,
} from './action'
// 请求github
function* getGithubInfo(action) {
  const { username } = action.payload
  // username为你的github 用户名
  const result = yield axios.get(`https://api.github.com/users/${username}`)
  // console.log(action, result, 'saga.....')
  yield put(setGithubInfo(result.data))
}
// 
function* watchCommon() {
  // 请求接口
  yield takeEvery(fetchGitInfo, getGithubInfo)
}
export default [fork(watchCommon)]

//3.reducer
import { handleActions } from 'redux-actions'
import {
  // 暂存github信息
  setGithubInfo,
} from './action'
// 该store的命名空间,可创建多个把store分开管理 
export const namespace = 'common'
export const defaultState = {
  // github个人信息
  githubInfo: {},
}
export const commonReducer = handleActions(
  {
    [setGithubInfo]: (state, action) => {
      const { githubData } = action.payload
      return { ...state, githubData }
    }
  },
  defaultState
)

// 4.reselect
// 从store单独获取githubInfo,实际中可能有N多个接口的不同数据
export const getGithubData = state => state[namespace].githubData || {}

// 5、组件内部使用
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import { fetchGitInfo } from '../../store/modules/common/action'
import { getGithubData } from '../../store/modules/common/selector'

const mapStateToProps = state => ({
  myGithubInfo: getGithubData(state),
})
const mapDispatchToProps = {
  fetchGitInfo,
}

const MyInfo = (props) => {
  const { myGithubInfo, fetchGitInfo } = props
  // react-hooks新增:可代替componentDidMount和componentDidUpdate
  useEffect(() => {
    if (myGithubInfo && !Object.keys(myGithubInfo).length) {
    // 触发action,开始请求接口
      fetchGitInfo('wjf444128852')
    }
  }, [myGithubInfo, fetchGitInfo])
  return (
    <div>
      <p>{myGithubInfo.name}</p>
      <p>{myGithubInfo.flowers}</p>
    </div>
  )
}

export default connect(mapStateToProps, mapDispatchToProps)(MyInfo)

  • 3、Material-table,定制thead,配置中文显示,配置参考(componenst/MaterialTable),支持数据导出功能。

// 一、materialTableConfig.js,可静态配置共用,可动态通过props根据需展示的数据格式定制
// 1.table-thbody-td的样式
const bodyCellStyle = {
  fontSize: '0.82rem',
  color: 'rgba(0, 0, 0, 0.87)',
  fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
  fontWeight: 300,
}

// 2.table-thead列数配置,与异步数据格式需一致
export const columnsArr = [
  { title: 'ID', field: 'id', cellStyle: bodyCellStyle },
  { title: '姓名', field: 'name', cellStyle: bodyCellStyle },
  { title: '年龄', field: 'age', cellStyle: bodyCellStyle },
  { title: '加入时间', field: 'joinTime', cellStyle: bodyCellStyle },
  { title: '到期时间', field: 'expiredTime', cellStyle: bodyCellStyle },
  { title: '生日', field: 'birthday', type: 'string', cellStyle: bodyCellStyle },
  {
    title: '出生地',
    field: 'birthCity',
    // birthCity id 
    lookup: { 1: '安徽', 2: '北京', 3: '上海', 4: '深圳', 5: '广州', 6: '杭州' },
    cellStyle: bodyCellStyle,
  },
]

// 3. 其他配置,
export const optionsSetting = {
  // thead style
  headerStyle: {
    // backgroundColor: '#66bb6a',
    ...bodyCellStyle,
    fontSize: '1rem',
    color: '#66bb6a',
  },
  rowStyle: {
    backgroundColor: '#EEE',
  },
  //是否需要导出报表CSV文件,按钮
  exportButton: true,
  //导出报表的名字
  exportFileName: '报表信息',
  // 操作按钮的位置,定位操作的位置,-1是末尾,默认开头
  actionsColumnIndex: -1,
  //是否需要开启分页
  paging: true,
  // 分页中每页数量,异步数据还需要配置total
  pageSize: 10,
}

// 4. table表格语言本地化配置,默认都是英文
export const localizationConfig = {
  header: {
    actions: '操作',
  },
  toolbar: {
    searchTooltip: '搜索',
    searchPlaceholder: '查找指定用户',
    exportTitle: '导出',
    exportName: '导出到CVS文件',
  },
  body: {
    emptyDataSourceMessage: '当前列表为空',
    addTooltip: '增加',
    editTooltip: '编辑',
    deleteTooltip: '删除',
    // 编辑选项提示设置
    editRow: {
      deleteText: '您确定要删除该选项吗?请您三思...',
      saveTooltip: '确定',
      cancelTooltip: '取消',
    }
  },
  pagination: {
    labelRowsSelect: '条',
    labelDisplayedRows: '{from}-{to} of {count}',
    // labelDisplayedRows: '{count}条',
    firstTooltip: '首页',
    previousTooltip: '上一页',
    nextTooltip: '下一页',
    lastTooltip: '尾页'
  }
}

//二、组件内使用

import { 
  // columnsArr,
  optionsSetting,
  localizationConfig,
} from './materialTableConfig'

<MaterialTable
 // 表格标题
  title=""
  // thead配置
  columns={columnsConfigArr}
  // 异步data
  data={data}
  // 其他配置
  options={optionsSetting}
  // 本地化语言显示设置
  localization={localizationConfig} />

目录结构


plop── 快速创建components和store的模板

     ┌── assets      资源文件
     ├── components  页面组件
     ├── router      路由配置
     ├── store       state模块管理中心
src──├── styles      页面样式
     ├
     ├── utils       插件和工具
     ├
     ├── views       与路由对应的页面
     └── index.js    页面配置入口
     
 
             ┌── Card             面板组件
             ├── CustomButtons    按钮组件
             ├── CustomInput      输入框组件
             ├── CustomTabs       公用Tab切换组件
components ──├── Dialog           弹框组件
             ├── Footer           底部footer
             ├── Grid             栅格组件
             ├── HeadNavBar       头部导航组件
             ├── HotelCard        酒店页面UI面板
             ├── HotelList        酒店页面列表UI组件
             ├── Login            登录组件
             ├── MaterialTable    定制可编辑Table组件
             ├── MuiDatepicker    日期选择器组件
             ├── MuiTimepicker    时间选择器组件
             ├── Notifications    自定义提示消息组件
             ├── Snackbar         Material-ui官方消息提示组件
             ├── Table            定制不可编辑的Table组件
             ├── Loading          loading组件
             ├── NotFound         404组件
             ├── ScrollToTopMount 路由切换缓动到顶部组件
             ├── SideBar          侧边栏路由导航
             └── SideTool         右边工具栏组件
             
             
       ┌── modules         不同的state模块
       ├     ├── account   登录验证state
       ├     ├── common    全局公用的state
       ├     └── theme     主题控制state
store──├
       └── indexStore.js   state入口
     

结语

欢迎来撩:444128852@qq.com

  • 1、上述中redux的工具使用相对复杂繁琐,且不适合react初学者!!!!
  • 2、实际中遇到的几个问题,A:react-loadable不在维护更新,已替换为@loadable/component;B:react-chartist官方图表使用的react版本是16.0,已向作者发PR,把componentWillReceiveProps替换为getDerivedStateFromProps C:打包部署到CDN二级目录的话,需要针针对路由添加配置,router basename 3 、以上只是实际开发中遇到的笔记总结,若有误请指出,如有用记得start~