├── public ├── favicon.ico ├── manifest.json └── index.html ├── src ├── actions │ └── index.js ├── utils │ ├── useConnect.js │ ├── baseUrl.js │ ├── loadable.js │ ├── connect.js │ ├── renderRoutes.js │ ├── asyncComponent.js │ ├── recursion-router.js │ ├── request.js │ └── index.js ├── stylus │ ├── index.less │ ├── common.less │ ├── loading.less │ └── reset.less ├── store │ ├── actionTypes.js │ ├── index.js │ ├── actionCreator.js │ └── reducer.js ├── pages │ ├── Home │ │ ├── index.less │ │ ├── children │ │ │ ├── One.js │ │ │ ├── Two.js │ │ │ └── Three.js │ │ └── index.js │ ├── User │ │ ├── Home │ │ │ └── index.js │ │ ├── Goods │ │ │ ├── GoodsList │ │ │ │ └── index.js │ │ │ └── GoodsClassify │ │ │ │ └── index.js │ │ ├── Permission │ │ │ ├── RoleManage │ │ │ │ └── index.js │ │ │ ├── UserManage │ │ │ │ └── index.js │ │ │ └── MenuManage │ │ │ │ └── index.js │ │ ├── OrderManage │ │ │ ├── ReturnGoods │ │ │ │ └── index.js │ │ │ ├── ProductManage │ │ │ │ ├── ReviewManage │ │ │ │ │ └── index.js │ │ │ │ └── ProductionList │ │ │ │ │ └── index.js │ │ │ └── OrderList │ │ │ │ └── index.js │ │ ├── index.less │ │ └── index.js │ ├── NotFound │ │ ├── index.less │ │ └── index.js │ └── Login │ │ ├── service.js │ │ ├── index.less │ │ └── index.js ├── App.test.js ├── index.css ├── setupProxy.js ├── App.js ├── index.js ├── common │ ├── SecondLevelComponent.js │ ├── ThirdLevelComponent.js │ ├── Etable │ │ └── index.js │ └── BaseForm │ │ └── index.js ├── config │ └── menuConfig.js ├── serviceWorker.js └── router.js ├── config ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── pnpTs.js ├── webpack.dll.conf.js ├── modules.js ├── paths.js ├── env.js ├── webpackDevServer.config.js └── webpack.config.js ├── .gitignore ├── README.md ├── scripts ├── test.js ├── start.js └── build.js └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loveRandy/react-admin/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | import * as all from '../store/actionCreator' 2 | 3 | export default all -------------------------------------------------------------------------------- /src/utils/useConnect.js: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | // import conncet from './connect' 3 | 4 | // export default @connect -------------------------------------------------------------------------------- /src/stylus/index.less: -------------------------------------------------------------------------------- 1 | //重置样式 2 | @import './reset.less'; 3 | //公用样式 4 | @import './common.less'; 5 | //loading样式 6 | @import './loading.less'; -------------------------------------------------------------------------------- /src/store/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const AUTH_CHANGE = 'auth_change'; 2 | export const PERMISSION_CHANGE = 'permission_change'; 3 | export const CURRENT_CHANGE = 'current_change'; -------------------------------------------------------------------------------- /src/pages/Home/index.less: -------------------------------------------------------------------------------- 1 | .logo { 2 | height: 32px; 3 | background: rgba(255, 255, 255, 0.2); 4 | margin: 16px; 5 | } 6 | .ant-breadcrumb > span:last-child a{ 7 | color:#000; 8 | } -------------------------------------------------------------------------------- /src/stylus/common.less: -------------------------------------------------------------------------------- 1 | .display_flex{ 2 | display:flex; 3 | } 4 | .flex_one{ 5 | flex:1; 6 | } 7 | .flex_center{ 8 | display:flex; 9 | justify-content: center; 10 | align-items: center; 11 | } 12 | -------------------------------------------------------------------------------- /src/pages/Home/children/One.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class One extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
one
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/Home/children/Two.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class Two extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
Two
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/Home/children/Three.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class Three extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
Three
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/User/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class Home extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
首页
8 | ) 9 | } 10 | } 11 | export default Home -------------------------------------------------------------------------------- /src/pages/User/Goods/GoodsList/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class GoodsList extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
GoodsList
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/User/Permission/RoleManage/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class RoleManage extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
RoleManage
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/User/Goods/GoodsClassify/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class GoodsClassify extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
GoodsClassify
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/User/OrderManage/ReturnGoods/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class ReturnGoods extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
ReturnGoods
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/NotFound/index.less: -------------------------------------------------------------------------------- 1 | .loading { 2 | display: flex; 3 | justify-content: center; 4 | /*x轴对齐方式*/ 5 | align-items: center; 6 | /*y轴对滴方式*/ 7 | height: calc(100vh - 250px); 8 | font-size:40px; 9 | /**屏幕高度百分百*/ 10 | 11 | } -------------------------------------------------------------------------------- /src/pages/User/OrderManage/ProductManage/ReviewManage/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class ReviewManage extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
ReviewManage
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from 'react-router-dom' 3 | 4 | export default class Home extends React.Component { 5 | render(){ 6 | return ( 7 | 8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/pages/User/OrderManage/ProductManage/ProductionList/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class ProductionList extends React.Component{ 4 | 5 | render(){ 6 | return ( 7 |
ProductionList
8 | ) 9 | } 10 | } -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/utils/baseUrl.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const proxyTargetMap = { 4 | prod: '/', 5 | randy: '/randy', 6 | peter: '/peter' 7 | } 8 | const API_TYPE = process.env.API_TYPE?process.env.API_TYPE:'randy' 9 | const baseUrl = process.env.NODE_ENV === 'production' ? '/' : proxyTargetMap[API_TYPE] 10 | export default baseUrl -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/utils/loadable.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Loadable from 'react-loadable'; 3 | 4 | //通用的过场组件 5 | const loadingComponent =()=>{ 6 | return ( 7 |
loading
8 | ) 9 | } 10 | 11 | //过场组件默认采用通用的,若传入了loading,则采用传入的过场组件 12 | export default (loader,loading = loadingComponent)=>{ 13 | return Loadable({ 14 | loader, 15 | loading 16 | }); 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } -------------------------------------------------------------------------------- /src/pages/Login/service.js: -------------------------------------------------------------------------------- 1 | import request from '../../utils/request' 2 | // 登陆 3 | export function login( data) { 4 | return request({ 5 | url: '/user/login', 6 | method: 'post', 7 | data, 8 | }) 9 | } 10 | // 2.获取商户支持的支付方式 11 | export function queryByOwenerIdAndOwnerType( params) { 12 | return request({ 13 | url: '/api/productSubscribe/queryByOwenerIdAndOwnerType', 14 | method: 'get', 15 | params, 16 | }) 17 | } -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore,applyMiddleware ,compose} from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import reducer from './reducer'; 4 | 5 | const composeEnhancers =window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose; 6 | const enhancer = composeEnhancers( 7 | applyMiddleware(thunk) 8 | ); 9 | 10 | const store = createStore(reducer, enhancer); 11 | export default store; -------------------------------------------------------------------------------- /src/pages/User/index.less: -------------------------------------------------------------------------------- 1 | .logo { 2 | height: 32px; 3 | background: rgba(255, 255, 255, 0.2); 4 | margin: 16px; 5 | } 6 | .ant-breadcrumb > span:last-child a{ 7 | color:#000; 8 | } 9 | .logoutIcon{ 10 | float: right; 11 | margin-right: 20px; 12 | font-size: 24px; 13 | color: #bbb; 14 | cursor: pointer; 15 | } 16 | .loginUser{ 17 | float: right; 18 | margin-right: 10px; 19 | font-size: 16px; 20 | color: #1890ff; 21 | } -------------------------------------------------------------------------------- /src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require('http-proxy-middleware') 2 | module.exports = function (app) { 3 | // ...You can now register proxies as you wish! 4 | app.use(proxy('/randy', { 5 | target: 'http://47.105.71.81:3306', 6 | secure: false, 7 | changeOrigin: true, 8 | pathRewrite: { 9 | "^/randy": "" 10 | }, 11 | })); 12 | app.use(proxy('/peter', { 13 | target: 'http://172.19.5.34:9531', 14 | secure: false, 15 | changeOrigin: true, 16 | pathRewrite: { 17 | "^/peter": "" 18 | }, 19 | })); 20 | }; -------------------------------------------------------------------------------- /src/utils/connect.js: -------------------------------------------------------------------------------- 1 | // import actions from '../actions' 2 | import * as action from '../store/actionCreator' 3 | import { connect } from 'react-redux' 4 | // import { bindActionCreators } from 'redux' 5 | export default connect( 6 | // state => ({state}), 7 | state => ({ 8 | state 9 | }), 10 | 11 | 12 | 13 | // dispatch => { 14 | // return bindActionCreators({ 15 | // action, 16 | // dispatch 17 | // }) 18 | 19 | // } 20 | dispatch =>{ 21 | return { 22 | ...action, 23 | dispatch 24 | } 25 | } 26 | 27 | ) 28 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderRoutes from './utils/renderRoutes' 3 | import routes from "./router.js"; 4 | import { HashRouter as Router, Switch } from "react-router-dom"; 5 | import connect from './utils/connect' 6 | 7 | @connect 8 | class App extends React.Component{ 9 | render(){ 10 | const authPath = '/login' // 默认未登录的时候返回的页面,可以自行设置 11 | let authed = this.props.state.authed || localStorage.getItem('authed') // 如果登陆之后可以利用redux修改该值 12 | return ( 13 | 14 | 15 | {renderRoutes(routes, authed, authPath)} 16 | 17 | 18 | ) 19 | } 20 | } 21 | 22 | export default App 23 | -------------------------------------------------------------------------------- /config/pnpTs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { resolveModuleName } = require('ts-pnp'); 4 | 5 | exports.resolveModuleName = ( 6 | typescript, 7 | moduleName, 8 | containingFile, 9 | compilerOptions, 10 | resolutionHost 11 | ) => { 12 | return resolveModuleName( 13 | moduleName, 14 | containingFile, 15 | compilerOptions, 16 | resolutionHost, 17 | typescript.resolveModuleName 18 | ); 19 | }; 20 | 21 | exports.resolveTypeReferenceDirective = ( 22 | typescript, 23 | moduleName, 24 | containingFile, 25 | compilerOptions, 26 | resolutionHost 27 | ) => { 28 | return resolveModuleName( 29 | moduleName, 30 | containingFile, 31 | compilerOptions, 32 | resolutionHost, 33 | typescript.resolveTypeReferenceDirective 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/utils/renderRoutes.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, Redirect, Switch } from 'react-router-dom' 3 | const renderRoutes = (routes, authed, authPath = '/login', extraProps = {}, switchProps = {}) => routes ? ( 4 | 5 | {routes.map((route, i) => ( 6 | { 12 | //如果是不需要权限 或者 已登录 或者 访问路径是/login,则直接返回当前组件 13 | if (!route.requiresAuth || authed || route.path === authPath) { 14 | return 15 | } 16 | //否则重定向到/login 17 | return 18 | }} 19 | /> 20 | ))} 21 | 22 | ) : null 23 | 24 | export default renderRoutes -------------------------------------------------------------------------------- /src/stylus/loading.less: -------------------------------------------------------------------------------- 1 | .ajax-loading { 2 | display: none; 3 | .loading { 4 | position: fixed; 5 | top: 50%; 6 | left: 50%; 7 | transform: translate(-50%, -50%); 8 | padding: 0 40px; 9 | height: 80px; 10 | line-height: 80px; 11 | background: rgba(0, 0, 0, 0.75); 12 | border-radius: 6px; 13 | text-align: center; 14 | z-index: 9999; 15 | font-size: 16px; 16 | color: #fff; 17 | img { 18 | width: 32px; 19 | vertical-align: middle; 20 | } 21 | span { 22 | margin-left: 12px; 23 | } 24 | } 25 | .overlay { 26 | position: fixed; 27 | left: 0; 28 | right: 0; 29 | top: 0; 30 | bottom: 0; 31 | z-index: 9998; 32 | background: rgb(255, 255, 255); 33 | opacity: 0.1; 34 | } 35 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | // import './index.css'; 5 | 6 | import './stylus/index.less' 7 | import App from './App'; 8 | import * as serviceWorker from './serviceWorker'; 9 | import { Provider } from 'react-redux' 10 | import store from './store' 11 | import {HashRouter, Route} from 'react-router-dom' 12 | 13 | const Main = () =>{ 14 | return ( 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | ReactDOM.render(
, document.getElementById('root')); 24 | 25 | // If you want your app to work offline and load faster, you can change 26 | // unregister() to register() below. Note this comes with some pitfalls. 27 | // Learn more about service workers: https://bit.ly/CRA-PWA 28 | serviceWorker.unregister(); 29 | -------------------------------------------------------------------------------- /src/pages/NotFound/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Spin } from 'antd' 3 | import './index.less' 4 | 5 | export default class NotFound extends React.Component { 6 | state= { 7 | status:true 8 | } 9 | 10 | componentDidMount(){ 11 | setTimeout(()=>{ 12 | this.setState({ 13 | status:false 14 | }) 15 | },1000) 16 | } 17 | 18 | componentWillUnmount(){ 19 | // 卸载异步操作设置状态 20 | this.setState = (state, callback) => { 21 | return; 22 | } 23 | } 24 | 25 | render(){ 26 | if(this.state.status){ 27 | return ( 28 |
29 | 30 |
31 | ) 32 | } 33 | return ( 34 |
35 | NotFound 404 36 |
37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 线上预览地址 2 | ### vue版本请移步 3 | 4 | #### 项目简介 5 | 根据不同的的角色展示不同的列表,项目里涵盖了后台管理系统里90%的需求,没有多余的东西,如果有自己的需求单独加上即可,如富文本,拖拽网上都有现成的 6 | #### 脚手架版本: 7 | *create-react-app@2 8 | 这个版本用webpack.config.js替换了webpack.config.prod.js和webpack.config.dev.js 9 | 10 | #### 用到react相关的生态链模块: 11 | * `react` 12 | * `react-dom` 13 | * `react-router-dom` 14 | * `redux`: 15 | * `react-redux` 16 | * `redux-actions` 17 | * `redux-thunk` 18 | * `antd` 19 | 20 | #### 项目要点 21 | * `less配置、antd按需加载` 22 | * `路由懒加载` 23 | * `根据权限生成动态路由` 24 | * `使用connect简化redux使用` 25 | * `全局数据请求拦截处理及loading` 26 | * `多个代理配置` 27 | * `常用表单封装、tabel封装` 28 | * `抽离第三方库文件dll` 29 | 30 | ### 项目启动步骤 31 | 1. 安装包 32 | cnpm/npm install 33 | 2. 开发运行 34 | npm run start 35 | 3. 生产打包 36 | npm run dll (仅需运行一次) 37 | npm run build 38 | 39 | 40 | ### 个人博客系统:www.randy168.com 41 | -------------------------------------------------------------------------------- /src/utils/asyncComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Spin } from "antd"; 3 | 4 | const asyncComponent = importComponent => { 5 | return class extends Component { 6 | constructor() { 7 | super(); 8 | this.state = { 9 | component: null 10 | }; 11 | } 12 | componentDidMount() { 13 | importComponent().then(cmp => { 14 | this.setState({ component: cmp.default }); 15 | }); 16 | } 17 | render() { 18 | const styleObj = { 19 | display: "flex", 20 | justifyContent: "center", 21 | alignItems: "center", 22 | height: "100vh", 23 | fontSize: "40px" 24 | }; 25 | const C = this.state.component; 26 | return C ? ( 27 | 28 | ) : ( 29 |
30 | 31 |
32 | ); 33 | } 34 | }; 35 | }; 36 | 37 | export default asyncComponent; 38 | -------------------------------------------------------------------------------- /src/stylus/reset.less: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | } 29 | ol, ul { 30 | list-style: none; 31 | } 32 | blockquote, q { 33 | quotes: none; 34 | } 35 | blockquote:before, blockquote:after, 36 | q:before, q:after { 37 | content: ''; 38 | content: none; 39 | } 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } -------------------------------------------------------------------------------- /src/pages/Login/index.less: -------------------------------------------------------------------------------- 1 | .wrapper_login{ 2 | width:100%; 3 | height:100vh; 4 | background:#2d3a4b; 5 | .login-form-login { 6 | width: 400px; 7 | .login-title{ 8 | font-size: 26px; 9 | color: #eee; 10 | margin: 0 auto 40px auto; 11 | text-align: center; 12 | font-weight: 700; 13 | } 14 | .login-form-button { 15 | width: 100%; 16 | height:40px; 17 | } 18 | #normal_login_username{ 19 | background: transparent; 20 | border: 1px solid hsla(0,0%,100%,.1); 21 | color: #fff; 22 | } 23 | #normal_login_password{ 24 | background: transparent; 25 | border: 1px solid hsla(0,0%,100%,.1); 26 | color: #fff; 27 | } 28 | #normal_login_password::placeholder{ 29 | color:#889aa4; 30 | } 31 | #normal_login_username::placeholder{ 32 | color:#889aa4; 33 | } 34 | .ant-input-affix-wrapper{ 35 | height:46px; 36 | } 37 | i{ 38 | color: #889aa4; 39 | } 40 | .loginTip{ 41 | color:#fff; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /config/webpack.dll.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const {CleanWebpackPlugin} = require('clean-webpack-plugin') 4 | // dll文件存放的目录 5 | const dllPath = '../public/vendor' 6 | 7 | module.exports = { 8 | entry: { 9 | // 需要提取的库文件 10 | vendor: ['react', 'react-redux', 'redux', 'react-router-dom', 'redux-actions','redux-thunk','axios','antd'] 11 | }, 12 | output: { 13 | path: path.join(__dirname, dllPath), 14 | // 保证每次打包出来的文件名都是唯一的 15 | filename: `[name].dll.${Math.ceil(Math.random() * 10000)}.js`, 16 | // vendor.dll.js中暴露出的全局变量名 17 | // 保持与 webpack.DllPlugin 中名称一致 18 | library: '[name]_[hash]' 19 | }, 20 | plugins: [ 21 | // 清除之前的dll文件 22 | new CleanWebpackPlugin({ 23 | root: path.join(__dirname, dllPath) 24 | }), 25 | // 设置环境变量 26 | new webpack.DefinePlugin({ 27 | 'process.env': { 28 | NODE_ENV: 'production' 29 | } 30 | }), 31 | // manifest.json 描述动态链接库包含了哪些内容 32 | new webpack.DllPlugin({ 33 | path: path.join(__dirname, dllPath, '[name]-manifest.json'), 34 | // 保持与 output.library 中名称一致 35 | name: '[name]_[hash]', 36 | context: process.cwd() 37 | }) 38 | ] 39 | } -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const camelcase = require('camelcase'); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFileName = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFileName}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/utils/recursion-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Array} userRouter 后台返回的用户权限json 4 | * @param {Array} allRouter 前端配置好的所有动态路由的集合 5 | * @return {Array} realRoutes 过滤后的路由 6 | */ 7 | 8 | //根据后台数据筛选出路由表 9 | export function recursionRouter(userRouter = [], allRouter = []) { 10 | const realRoutes = allRouter 11 | .filter(item => userRouter.includes(item.pathName)) 12 | .map(item => ({ 13 | ...item, 14 | children: item.children 15 | ? recursionRouter(userRouter, item.children) 16 | : null 17 | })) 18 | return realRoutes 19 | } 20 | 21 | //重定向到children的第一个路由 22 | export function recursionRouterTwo(userRouter = [], allRouter = []) { 23 | const realRoutes = allRouter 24 | .filter(item => userRouter.includes(item.path)) 25 | .map(item =>{ 26 | return { 27 | ...item, 28 | redirect:item.children?item.children[0].path:null, 29 | children: item.children 30 | ? recursionRouterTwo(userRouter, item.children) 31 | : null 32 | } 33 | }) 34 | return realRoutes 35 | 36 | } 37 | 38 | export function recursionRouterThree(userRouter = [], allRouter = []) { 39 | let list = [] 40 | allRouter.forEach((item,index) =>{ 41 | if(item.path === userRouter[0]){ 42 | list.push(item) 43 | } 44 | }) 45 | return list 46 | 47 | 48 | 49 | } -------------------------------------------------------------------------------- /src/common/SecondLevelComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, Switch } from "react-router-dom"; 3 | import NotFound from '../pages/NotFound' 4 | import connect from '../utils/connect' 5 | @connect 6 | class SecondLevelComponent extends React.Component{ 7 | 8 | render(){ 9 | 10 | const permissionList = this.props.state.permissionList 11 | const path = this.props.match.path 12 | const currentList = permissionList.filter(item =>{ 13 | return item.path === path 14 | }) 15 | let list = [] 16 | if( currentList.length>0 && currentList[0].children ){ 17 | list = currentList[0].children.map((item,index) => { 18 | return ( 19 | 26 | ) 27 | }) 28 | return ( 29 | 30 | { 31 | list 32 | } 33 | 34 | 35 | ) 36 | }else{ 37 | return ( 38 |
...
39 | ) 40 | } 41 | 42 | 43 | } 44 | } 45 | export default SecondLevelComponent -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | 19 | const jest = require('jest'); 20 | const execSync = require('child_process').execSync; 21 | let argv = process.argv.slice(2); 22 | 23 | function isInGitRepository() { 24 | try { 25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); 26 | return true; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | function isInMercurialRepository() { 33 | try { 34 | execSync('hg --cwd . root', { stdio: 'ignore' }); 35 | return true; 36 | } catch (e) { 37 | return false; 38 | } 39 | } 40 | 41 | // Watch unless on CI or explicitly running all tests 42 | if ( 43 | !process.env.CI && 44 | argv.indexOf('--watchAll') === -1 45 | ) { 46 | // https://github.com/facebook/create-react-app/issues/5210 47 | const hasSourceControl = isInGitRepository() || isInMercurialRepository(); 48 | argv.push(hasSourceControl ? '--watch' : '--watchAll'); 49 | } 50 | 51 | 52 | jest.run(argv); 53 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | randy 23 | 24 | 25 | 26 |
27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/common/ThirdLevelComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, Switch } from "react-router-dom"; 3 | import NotFound from '../pages/NotFound' 4 | import connect from '../utils/connect' 5 | @connect 6 | 7 | class ThirdLevelComponent extends React.Component{ 8 | 9 | render(){ 10 | const permissionList = this.props.state.permissionList 11 | const path = this.props.match.path 12 | const item = permissionList.find(item =>{ 13 | return path.indexOf(item.path) !== -1 14 | }) 15 | const currentList = item.children.filter(item =>{ 16 | return item.path === path 17 | }) 18 | let list = [] 19 | if( currentList.length>0 && currentList[0].children ){ 20 | list = currentList[0].children.map((item,index) => { 21 | return ( 22 | 29 | ) 30 | }) 31 | return ( 32 |
33 | 34 | { 35 | list 36 | } 37 | 38 | 39 |
40 | 41 | ) 42 | }else{ 43 | return ( 44 |
...
45 | ) 46 | } 47 | } 48 | } 49 | export default ThirdLevelComponent -------------------------------------------------------------------------------- /src/config/menuConfig.js: -------------------------------------------------------------------------------- 1 | const menuList = [ 2 | // { 3 | // title: '首页', 4 | // key: '/home' 5 | // }, 6 | { 7 | title: '订单管理', 8 | key: '/user/order', 9 | children: [ 10 | { 11 | title: '订单列表', 12 | key: '/user/order/list', 13 | }, 14 | { 15 | title: '退货管理', 16 | key: '/user/order/returnGoods', 17 | }, 18 | { 19 | title: '生产管理', 20 | key: '/user/order/product', 21 | children:[ 22 | { 23 | title: '生产列表', 24 | key: '/user/order/product/list', 25 | }, 26 | { 27 | title: '审核管理', 28 | key: '/user/order/product/review', 29 | } 30 | ] 31 | } 32 | ] 33 | }, 34 | { 35 | title: '产品管理', 36 | key: '/user/goods', 37 | children:[ 38 | { 39 | title: '产品列表', 40 | key: '/user/goods/list', 41 | }, 42 | { 43 | title: '产品分类', 44 | key: '/user/goods/classify', 45 | } 46 | ] 47 | 48 | }, 49 | { 50 | title: '权限管理', 51 | key: '/user/permission', 52 | children:[ 53 | { 54 | title: '用户管理', 55 | key: '/user/permission/user', 56 | }, 57 | { 58 | title: '角色管理', 59 | key: '/user/permission/role', 60 | }, 61 | { 62 | title: '菜单管理', 63 | key: '/user/permission/menu', 64 | } 65 | ] 66 | } 67 | ]; 68 | export default menuList; -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import baseURL from './baseUrl' 3 | import { getLocal } from '../utils' 4 | import { authChangeAction } from '../store/actionCreator' 5 | import store from '../store' 6 | 7 | //创建axios实例 8 | const service = axios.create({ 9 | baseURL: baseURL, // api的base_url 10 | timeout: 200000, // 请求超时时间 11 | withCredentials: true // 选项表明了是否是跨域请求 12 | }) 13 | service.interceptors.request.use(config => { 14 | // 请求头添加token 15 | if (getLocal('authed')) { 16 | config.headers.Authorization = `Bearer ${getLocal('authed')}` 17 | } 18 | const flag = (config.data && config.data.loading !==false) || (config.params && config.params.loading !== false) 19 | if(flag){ 20 | let loading 21 | loading = document.getElementById('ajaxLoading') 22 | loading.style.display = 'block' 23 | } 24 | return config; 25 | }, err => { 26 | console.log('请求失败') 27 | return Promise.reject(err) 28 | }) 29 | 30 | 31 | 32 | //拦截响应 33 | service.interceptors.response.use(config => { 34 | if(config.data && config.data.loading !==false){ 35 | let loading 36 | loading = document.getElementById('ajaxLoading') 37 | loading.style.display = 'none' 38 | } 39 | return config; 40 | }, err => { 41 | console.log('响应失败') 42 | return Promise.reject(err) 43 | }) 44 | 45 | 46 | 47 | // respone拦截器 48 | service.interceptors.response.use( 49 | response => { 50 | const res = response.data 51 | if (res.code !== 1) { 52 | res.code = res.data.code 53 | res.message = res.response.data.msg 54 | return Promise.reject('error') 55 | } else { 56 | return response.data 57 | } 58 | }, 59 | error => { 60 | const { status } = error.response 61 | switch (status) { 62 | case 401: 63 | store.dispatch(authChangeAction(null)) 64 | break; 65 | 66 | default: 67 | break; 68 | } 69 | return Promise.reject(error) 70 | } 71 | ) 72 | export default service 73 | -------------------------------------------------------------------------------- /src/store/actionCreator.js: -------------------------------------------------------------------------------- 1 | import { AUTH_CHANGE,PERMISSION_CHANGE, CURRENT_CHANGE } from './actionTypes' 2 | import { createActions } from 'redux-actions'; 3 | import { recursionRouter } from '../utils/recursion-router' 4 | import routes from '../router' 5 | import request from '../utils/request' 6 | import { filterRoutes } from '../utils' 7 | import { recursionRouterThree } from '../utils/recursion-router' 8 | 9 | // export const doAuthChangeAction = (res) => { 10 | // return { 11 | // type:AUTH_CHANGE, 12 | // authStatus:res 13 | // } 14 | // } 15 | export const doAuthChangeAction = createActions( 16 | { 17 | [AUTH_CHANGE]:(res) => { 18 | return { 19 | authStatus:res 20 | } 21 | }, 22 | [PERMISSION_CHANGE]:(permissionList,currentList,avatar,name) => { 23 | return { 24 | permissionList, 25 | currentList, 26 | avatar, 27 | name 28 | } 29 | }, 30 | [CURRENT_CHANGE]:(list) => { 31 | return { 32 | currentList:list 33 | } 34 | } 35 | } 36 | ) 37 | 38 | export const authChangeAction = (token) =>{ 39 | return (dispatch) =>{ 40 | const action = doAuthChangeAction.authChange(token) 41 | dispatch(action) 42 | } 43 | } 44 | 45 | export const permissionAction = (path) =>{ 46 | return (dispatch) =>{ 47 | request({ 48 | url: '/user/info', 49 | method: 'get', 50 | }) 51 | .then(res =>{ 52 | const allList = routes[2].children 53 | res.data.data.push('index')//把首页丢进去 54 | const permissionList = recursionRouter(res.data.data,allList) 55 | 56 | const defaultOpenKeys = filterRoutes(path) 57 | const currentList = recursionRouterThree(defaultOpenKeys,permissionList) 58 | const action = doAuthChangeAction.permissionChange(permissionList,currentList,res.data.avatar,res.data.name) 59 | dispatch(action) 60 | 61 | 62 | }) 63 | } 64 | } 65 | 66 | export const currentAction = (list) =>{ 67 | return (dispatch) =>{ 68 | const action = doAuthChangeAction.currentChange(list) 69 | dispatch(action) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/store/reducer.js: -------------------------------------------------------------------------------- 1 | import { AUTH_CHANGE, PERMISSION_CHANGE,CURRENT_CHANGE } from './actionTypes' 2 | import { handleActions } from 'redux-actions' 3 | import Index from '../pages/User/Home' 4 | const defaultState = { 5 | authed: false, 6 | permissionList:[ 7 | { 8 | path: '/user/index', 9 | pathName:'index', 10 | name:'首页', 11 | component:Index, 12 | icon:'pie-chart' 13 | } 14 | ], 15 | currentList:[], 16 | avatar:'', 17 | name:'' 18 | }; 19 | 20 | export const statusReducer = handleActions( 21 | { 22 | [AUTH_CHANGE]:(state, action)=> { 23 | const newState = JSON.parse(JSON.stringify(state)) 24 | newState.authed = action.payload.authStatus 25 | if(newState.authed !== null){ 26 | localStorage.setItem('authed',newState.authed) 27 | }else{ 28 | newState.permissionList = [ 29 | { 30 | path: '/user/index', 31 | pathName:'index', 32 | name:'首页', 33 | component:Index, 34 | icon:'pie-chart' 35 | } 36 | ] 37 | localStorage.removeItem('authed') 38 | } 39 | 40 | return newState; 41 | }, 42 | [PERMISSION_CHANGE]:(state, action)=> { 43 | const newState = JSON.parse(JSON.stringify(state)) 44 | newState.permissionList = action.payload.permissionList 45 | newState.currentList = action.payload.currentList 46 | newState.avatar = action.payload.avatar 47 | newState.name = action.payload.name 48 | return newState; 49 | }, 50 | [CURRENT_CHANGE]:(state, action)=> { 51 | const newState = JSON.parse(JSON.stringify(state)) 52 | newState.currentList = action.payload.currentList 53 | return newState; 54 | } 55 | 56 | },defaultState) 57 | 58 | export default statusReducer 59 | 60 | // export default (state = defaultState, action) => { 61 | // if(action.type === AUTH_CHANGE){ 62 | // const newState = JSON.parse(JSON.stringify(state)) 63 | // newState.authed = action.authStatus 64 | // return newState; 65 | // } 66 | 67 | // return state; 68 | // }; 69 | -------------------------------------------------------------------------------- /src/common/Etable/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Table } from 'antd' 3 | class Etable extends React.Component { 4 | 5 | onSelectChange = (selectedRowKeys,selectedRows)=> { 6 | this.props.updateSelectedItem(selectedRowKeys,selectedRows,this.props.that) 7 | } 8 | onRowClick = (index,record)=>{ 9 | /** 10 | * @param {* 勾选的类型} type radio是单选 11 | */ 12 | const type = this.props.type 13 | if(type=== 'radio'){ 14 | this.onSelectChange([index],[record]) 15 | }else{ 16 | let selectedRowKeys = [...this.props.rowSelection.selectedRowKeys] 17 | let selectedRows = [...this.props.rowSelection.selectedRows] 18 | if(selectedRowKeys.includes(index)){ 19 | var selectIndex = selectedRowKeys.findIndex(item=>{ 20 | return index===item 21 | }) 22 | selectedRowKeys.splice(selectIndex,1) 23 | selectedRows.splice(selectIndex,1) 24 | }else{ 25 | selectedRowKeys.push(index) 26 | selectedRows.push(record) 27 | } 28 | this.onSelectChange(selectedRowKeys,selectedRows) 29 | } 30 | 31 | 32 | } 33 | render(){ 34 | 35 | //是否分页 36 | let pagination = this.props.pagination 37 | if(!pagination || pagination === false){ 38 | pagination = false 39 | } 40 | 41 | //是否需要勾选 42 | let rowSelection = this.props.rowSelection 43 | rowSelection.type = this.props.type==='radio'?'radio':'checkbox' 44 | let onRow 45 | if(!rowSelection){ 46 | rowSelection = null 47 | }else{ 48 | rowSelection.onChange = this.onSelectChange; 49 | onRow = (record,index) =>{ 50 | return { 51 | onClick:()=>{ 52 | this.onRowClick(index,record) 53 | } 54 | } 55 | } 56 | } 57 | 58 | 59 | return ( 60 | 67 | ) 68 | } 69 | 70 | } 71 | 72 | export default Etable -------------------------------------------------------------------------------- /config/modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | 8 | /** 9 | * Get the baseUrl of a compilerOptions object. 10 | * 11 | * @param {Object} options 12 | */ 13 | function getAdditionalModulePaths(options = {}) { 14 | const baseUrl = options.baseUrl; 15 | 16 | // We need to explicitly check for null and undefined (and not a falsy value) because 17 | // TypeScript treats an empty string as `.`. 18 | if (baseUrl == null) { 19 | // If there's no baseUrl set we respect NODE_PATH 20 | // Note that NODE_PATH is deprecated and will be removed 21 | // in the next major release of create-react-app. 22 | 23 | const nodePath = process.env.NODE_PATH || ''; 24 | return nodePath.split(path.delimiter).filter(Boolean); 25 | } 26 | 27 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 28 | 29 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is 30 | // the default behavior. 31 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { 32 | return null; 33 | } 34 | 35 | // Allow the user set the `baseUrl` to `appSrc`. 36 | if (path.relative(paths.appSrc, baseUrlResolved) === '') { 37 | return [paths.appSrc]; 38 | } 39 | 40 | // Otherwise, throw an error. 41 | throw new Error( 42 | chalk.red.bold( 43 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 44 | ' Create React App does not support other values at this time.' 45 | ) 46 | ); 47 | } 48 | 49 | function getModules() { 50 | // Check if TypeScript is setup 51 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 52 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 53 | 54 | if (hasTsConfig && hasJsConfig) { 55 | throw new Error( 56 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 57 | ); 58 | } 59 | 60 | let config; 61 | 62 | // If there's a tsconfig.json we assume it's a 63 | // TypeScript project and set up the config 64 | // based on tsconfig.json 65 | if (hasTsConfig) { 66 | config = require(paths.appTsConfig); 67 | // Otherwise we'll check if there is jsconfig.json 68 | // for non TS projects. 69 | } else if (hasJsConfig) { 70 | config = require(paths.appJsConfig); 71 | } 72 | 73 | config = config || {}; 74 | const options = config.compilerOptions || {}; 75 | 76 | const additionalModulePaths = getAdditionalModulePaths(options); 77 | 78 | return { 79 | additionalModulePaths: additionalModulePaths, 80 | hasTsConfig, 81 | }; 82 | } 83 | 84 | module.exports = getModules(); 85 | -------------------------------------------------------------------------------- /src/pages/Login/index.js: -------------------------------------------------------------------------------- 1 | import { Form, Icon, Input, Button } from 'antd' 2 | import React from 'react' 3 | import { Redirect } from 'react-router-dom' 4 | import connect from '../../utils/connect' 5 | import { login } from './service' 6 | import './index.less' 7 | @connect 8 | class NormalLoginForm extends React.Component { 9 | handleSubmit = e => { 10 | const _this = this 11 | e.preventDefault(); 12 | this.props.form.validateFields((err, values) => { 13 | if (!err) { 14 | _this.authChange(values) 15 | }else{ 16 | console.log(err) 17 | } 18 | }) 19 | } 20 | 21 | authChange = (values)=>{ 22 | const { dispatch, authChangeAction } = this.props 23 | login(values).then(res =>{ 24 | const action = authChangeAction(res.data.token) 25 | dispatch(action) 26 | }) 27 | } 28 | 29 | render() { 30 | if(this.props.state.authed ||localStorage.getItem('authed')){ 31 | return ( 32 | 33 | ) 34 | } 35 | const { getFieldDecorator } = this.props.form; 36 | return ( 37 |
38 |
39 |
后台管理系统
40 | 41 | {getFieldDecorator('username', { 42 | rules: [{ required: true, message: 'Please input your username!' }], 43 | })( 44 | } 46 | placeholder="username" 47 | />, 48 | )} 49 | 50 | 51 | {getFieldDecorator('password', { 52 | rules: [{ required: true, message: 'Please input your Password!' }], 53 | })( 54 | } 56 | type="password" 57 | placeholder="password" 58 | />, 59 | )} 60 | 61 | 62 | 65 | 66 | 67 |
用户为admin的时候,能够看到所有的权限列表,其余账号只能看到部分
68 |
69 | 70 |
71 | ); 72 | } 73 | } 74 | 75 | const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm) 76 | export default WrappedNormalLoginForm 77 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right