├── src ├── scripts │ ├── actions │ │ ├── sysmanage │ │ │ ├── SysUsers.js │ │ │ ├── SysIndex.js │ │ │ └── SysProjects.js │ │ ├── index.js │ │ └── myprojects │ │ │ └── MyIndex.js │ ├── reducers │ │ ├── myprojects │ │ │ ├── MyProjects.js │ │ │ └── MyIndex.js │ │ ├── index.js │ │ └── sysmanage │ │ │ ├── SysProjects.js │ │ │ └── SysIndex.js │ ├── services │ │ ├── ProjectService.js │ │ └── projects.json │ ├── store │ │ └── index.js │ ├── index.js │ ├── components │ │ ├── common │ │ │ ├── roles │ │ │ │ └── Roles.js │ │ │ ├── resources │ │ │ │ ├── ResAPI.js │ │ │ │ ├── ResURL.js │ │ │ │ ├── ResInput.js │ │ │ │ ├── ResourceList.js │ │ │ │ └── Resources.js │ │ │ ├── users │ │ │ │ ├── UserInfoDialog.js │ │ │ │ ├── UserInfo.js │ │ │ │ ├── UserList.js │ │ │ │ └── Users.js │ │ │ ├── ProjectCreate.js │ │ │ ├── ProjectList.js │ │ │ ├── NavBar.js │ │ │ └── projects │ │ │ │ └── ProjectInfo.js │ │ ├── myprojects │ │ │ ├── MyProjects.js │ │ │ └── MyIndex.js │ │ ├── sysmanage │ │ │ ├── SysProjects.js │ │ │ └── SysIndex.js │ │ └── header │ │ │ └── Header.js │ ├── constants │ │ ├── ActionTypes.js │ │ └── Links.js │ ├── containers │ │ └── App.js │ ├── common │ │ ├── utils │ │ │ ├── HistoryFly.js │ │ │ └── ReduxConnect.js │ │ └── ui │ │ │ ├── ToolBar.js │ │ │ ├── DropSelect.js │ │ │ ├── DataTable.js │ │ │ └── Dialog.js │ └── AppRouter.js └── styles │ ├── components │ ├── projects │ │ ├── project.css │ │ └── projects.css │ ├── resources │ │ └── resource.css │ ├── users │ │ └── users.css │ └── header │ │ ├── header.css │ │ └── navbar.css │ ├── common │ ├── index.css │ ├── optsbar.css │ ├── breadcrumb.css │ └── toolbar.css │ ├── containers │ └── app.css │ └── index.css ├── README.md ├── html └── index.html ├── .gitignore ├── server.js ├── webpack.server.js ├── pom.xml ├── webpack.dll.js ├── package.json ├── webpack.prod.js ├── webpack.base.js ├── webpack.dev.js └── dll └── vendors_manifest.json /src/scripts/actions/sysmanage/SysUsers.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scripts/reducers/myprojects/MyProjects.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/components/projects/project.css: -------------------------------------------------------------------------------- 1 | .project-wrap { 2 | flex: 1; 3 | display: flex; 4 | flex-direction: column; 5 | } -------------------------------------------------------------------------------- /src/styles/common/index.css: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | @import './toolbar.css'; 4 | @import './optsbar.css'; 5 | @import './breadcrumb.css'; -------------------------------------------------------------------------------- /src/styles/common/optsbar.css: -------------------------------------------------------------------------------- 1 | $proj-toolbar: 60px; 2 | 3 | .opts-bar { 4 | height: $proj-toolbar; 5 | line-height: $proj-toolbar; 6 | background: #edf1f2; 7 | border-bottom: 1px solid #dee5e7; 8 | padding-left: 20px; 9 | } -------------------------------------------------------------------------------- /src/scripts/actions/index.js: -------------------------------------------------------------------------------- 1 | import * as SysIndex from './sysmanage/SysIndex'; 2 | import * as SysProjects from './sysmanage/SysProjects'; 3 | import * as MyIndex from './myprojects/MyIndex'; 4 | 5 | export default { 6 | SysIndex, 7 | SysProjects, 8 | MyIndex 9 | } -------------------------------------------------------------------------------- /src/scripts/actions/sysmanage/SysIndex.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | 3 | /** 4 | * 系统管理 -- 切换导航 5 | * @param {[type]} curNavId 当前点击的导航的 ID 6 | * @return {[type]} [description] 7 | */ 8 | export function switchNavItem (curNavId) { 9 | return { 10 | type: act.SYS_SWITCH_NAV, 11 | curNavId 12 | }; 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###FE project seed 2 | webpack + reactjs + redux + postcss 3 | ###local dev environment 4 | node >=4.4.7 5 | npm >=2.15.8 6 | ###project initialize 7 | npm run init 8 | ###run project 9 | open one command window and run (project root dir) : node server 10 | open another command window and run (project root dir) : npm run dev -------------------------------------------------------------------------------- /src/scripts/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import SysProjects from './sysmanage/SysProjects'; 4 | import SysIndex from './sysmanage/SysIndex'; 5 | import MyIndex from './myprojects/MyIndex'; 6 | 7 | const rootReducer = combineReducers({ 8 | SysIndex, 9 | SysProjects, 10 | MyIndex 11 | }); 12 | 13 | export default rootReducer; -------------------------------------------------------------------------------- /src/scripts/actions/sysmanage/SysProjects.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | import ProjectService from 'services/ProjectService'; 3 | 4 | 5 | // 获取项目列表 6 | export function fetchProjects () { 7 | return (dispatch) => { 8 | ProjectService.getProjects().then(projects => { 9 | dispatch({ 10 | type: act.SYS_FETCH_PROJECTS, 11 | projects 12 | }); 13 | }); 14 | }; 15 | } -------------------------------------------------------------------------------- /src/scripts/services/ProjectService.js: -------------------------------------------------------------------------------- 1 | import projectData from './projects.json'; 2 | 3 | const TIMEOUT = 100; 4 | 5 | export default { 6 | 7 | // 获取所有的项目 8 | getProjects() { 9 | return new Promise((resolve) => { 10 | setTimeout(() => { resolve(projectData.data); }, TIMEOUT); 11 | }); 12 | }, 13 | 14 | // 新增项目 15 | addProject(data, cb) { 16 | setTimeout(() => cb(), TIMEOUT); 17 | } 18 | 19 | }; -------------------------------------------------------------------------------- /src/scripts/store/index.js: -------------------------------------------------------------------------------- 1 | const { createStore, applyMiddleware } = Redux; 2 | const thunk = ReduxThunk.default; 3 | import createLogger from 'redux-logger'; 4 | import rootReducer from '../reducers'; 5 | 6 | export default function configureStore(preloadedState) { 7 | const store = createStore( 8 | rootReducer, 9 | preloadedState, 10 | applyMiddleware(thunk, createLogger()) 11 | ); 12 | 13 | return store; 14 | } -------------------------------------------------------------------------------- /src/styles/containers/app.css: -------------------------------------------------------------------------------- 1 | .pageframe-wrap { 2 | height: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | 6 | a { 7 | color: inherit; 8 | } 9 | 10 | @import '../components/header/header.css'; 11 | @import '../components/header/navbar.css'; 12 | @import '../components/projects/projects.css'; 13 | @import '../components/projects/project.css'; 14 | @import '../components/users/users.css'; 15 | } -------------------------------------------------------------------------------- /src/scripts/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 入口文件 3 | */ 4 | 5 | // 引入样式 6 | import 'styles/index.css'; 7 | 8 | // 引入 provider 组件 9 | const { Provider } = ReactRedux; 10 | 11 | // redux store 12 | import configureStore from './store'; 13 | import AppRouter from './AppRouter'; 14 | 15 | const store = configureStore(); 16 | 17 | // 渲染页面 18 | ReactDOM.render(( 19 | 20 | 21 | 22 | ), document.querySelector('.main')); -------------------------------------------------------------------------------- /src/styles/components/resources/resource.css: -------------------------------------------------------------------------------- 1 | .resource-wrap { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | .reso-header { 6 | height: 49px; 7 | padding-left: 10px; 8 | background: #edf1f2; 9 | border-bottom: 1px solid #b9b9b9; 10 | 11 | .ant-menu { 12 | margin-top: 1px; 13 | background: #edf1f2; 14 | } 15 | } 16 | 17 | .reso-body { 18 | flex: 1; 19 | padding: 10px; 20 | } 21 | } -------------------------------------------------------------------------------- /src/scripts/components/common/roles/Roles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // import Component from './Component'; 6 | 7 | 8 | export default class Roles extends React.Component { 9 | 10 | defaultProps = { 11 | 12 | } 13 | 14 | state = { 15 | 16 | } 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | render() { 23 | return ( 24 |
Roles
25 | ); 26 | } 27 | 28 | componentDidMount() { 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | demo 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/scripts/components/common/resources/ResAPI.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // import Component from './Component'; 6 | 7 | 8 | export default class ResAPI extends React.Component { 9 | 10 | static defaultProps = { 11 | 12 | } 13 | 14 | state = { 15 | 16 | } 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | render() { 23 | return ( 24 |
ResAPI
25 | ); 26 | } 27 | 28 | componentDidMount() { 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/scripts/components/common/resources/ResURL.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // import Component from './Component'; 6 | 7 | 8 | export default class ResURL extends React.Component { 9 | 10 | static defaultProps = { 11 | 12 | } 13 | 14 | state = { 15 | 16 | } 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | render() { 23 | return ( 24 |
ResURL
25 | ); 26 | } 27 | 28 | componentDidMount() { 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/scripts/components/common/resources/ResInput.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | 5 | // import Component from './Component'; 6 | 7 | 8 | export default class ResInput extends React.Component { 9 | 10 | static defaultProps = { 11 | 12 | } 13 | 14 | state = { 15 | 16 | } 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | render() { 23 | return ( 24 |
ResInput
25 | ); 26 | } 27 | 28 | componentDidMount() { 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/scripts/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目中所有的 action type 3 | * @type {String} 4 | */ 5 | 6 | // SysMange action 7 | export const SYS_SWITCH_NAV = 'SYS_SWITCH_NAV'; // 切换导航按钮 8 | 9 | // SysProjects action 10 | export const SYS_FETCH_PROJECTS = 'SYS_FETCH_PROJECTS'; // 获取项目列表 11 | 12 | // MyProjects action 13 | export const MY_SWITCH_PROJECT = 'MY_SWITCH_PROJECT'; // 切换我的项目 14 | export const MY_FETCH_PROJECTS = 'MY_FETCH_PROJECTS'; // 获取我的项目列表 15 | export const MY_SWITCH_NAV = 'MY_SWITCH_NAV'; // 我的项目切换导航 16 | 17 | -------------------------------------------------------------------------------- /src/scripts/containers/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import * as actions from 'actions'; 5 | import Header from 'components/header/Header'; 6 | 7 | 8 | export default class App extends React.Component { 9 | 10 | constructor() { 11 | super() 12 | } 13 | 14 | render() { 15 | return ( 16 |
17 |
18 | { this.props.children } 19 |
20 | ); 21 | } 22 | 23 | componentDidMount() { 24 | 25 | } 26 | 27 | } 28 | 29 | export default App; -------------------------------------------------------------------------------- /src/scripts/common/utils/HistoryFly.js: -------------------------------------------------------------------------------- 1 | let { browserHistory } = ReactRouter; 2 | 3 | 4 | export default { 5 | /** 6 | * 跳转到指定的路径下 7 | * @param {[type]} path [description] 8 | * @param {[type]} id [description] 9 | * @return {[type]} [description] 10 | */ 11 | go (path, id) { 12 | if (id) { 13 | path = path.replace(':id', id); 14 | } 15 | browserHistory.push(path); 16 | }, 17 | 18 | back () { 19 | browserHistory.goBack(); 20 | }, 21 | 22 | forward () { 23 | browserHistory.goForward(); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dev/ 2 | /prd/ 3 | /refs/ 4 | 5 | 6 | # kdiff3 ignore 7 | *.orig 8 | 9 | # maven ignore 10 | target/ 11 | 12 | # eclipse ignore 13 | .settings/ 14 | .project 15 | .classpath 16 | 17 | # idea ignore 18 | .idea/ 19 | *.ipr 20 | *.iml 21 | *.iws 22 | 23 | # temp ignore 24 | *.log 25 | *.cache 26 | *.diff 27 | *.patch 28 | *.tmp 29 | 30 | # system ignore 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # package ignore (optional) 35 | # *.jar 36 | # *.war 37 | # *.zip 38 | # *.tar 39 | # *.tar.gz 40 | 41 | 42 | .sass-cache 43 | /resource/ 44 | /log/ 45 | /node_modules/ -------------------------------------------------------------------------------- /src/styles/common/breadcrumb.css: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | height: $proj-breadcrumb-h; 3 | line-height: $proj-breadcrumb-h; 4 | background: $dark-bgc; 5 | padding-left: 30px; 6 | color: $light-bgc; 7 | 8 | .proj-breadcrumb { 9 | span { 10 | padding: 0 3px; 11 | } 12 | .breadcrumb-arrow .anticon { 13 | opacity: 0.5; 14 | transform: scale(0.8); 15 | } 16 | .breadcrumb-home { 17 | cursor: pointer; 18 | .anticon { 19 | font-weight: bold; 20 | cursor: pointer; 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/scripts/constants/Links.js: -------------------------------------------------------------------------------- 1 | export const MY_PROJECTS = '/page/myprojects'; 2 | export const MY_PROJECT_HREF = '/page/myprojects/:id'; 3 | export const MY_PROJECT_USERS = '/page/myprojects/:id/users'; 4 | export const MY_PROJECT_ROLES = '/page/myprojects/:id/roles'; 5 | export const MY_PROJECT_RESOURCES = '/page/myprojects/:id/resources'; 6 | 7 | export const SYS_MANAGE = '/page/sysmanage'; 8 | export const SYS_PROJECTS = '/page/sysmanage/projects'; 9 | export const SYS_USERS = '/page/sysmanage/users'; 10 | export const SYS_ROLES = '/page/sysmanage/roles'; 11 | export const SYS_RESOURCES = '/page/sysmanage/resources'; 12 | -------------------------------------------------------------------------------- /src/scripts/services/projects.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "key": "p001", 5 | "title": "项目一运营专题活动系统系统系同学他", 6 | "userCount": 100, 7 | "roleCount": 10, 8 | "resourceCount": 200 9 | }, 10 | { 11 | "key": "p002", 12 | "title": "项目2", 13 | "userCount": 120, 14 | "roleCount": 12, 15 | "resourceCount": 220 16 | }, 17 | { 18 | "key": "p003", 19 | "title": "项目3", 20 | "userCount": 130, 21 | "roleCount": 13, 22 | "resourceCount": 230 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /src/styles/components/users/users.css: -------------------------------------------------------------------------------- 1 | $user-info-w: 300px; 2 | 3 | .user-wrap { 4 | flex: 1; 5 | display: flex; 6 | flex-direction: column; 7 | 8 | .user-list-wrap { 9 | flex: 1; 10 | position: relative; 11 | 12 | .user-list { 13 | position: absolute; 14 | width: 100%; 15 | top: 0; 16 | bottom: 0; 17 | padding: 0 15px; 18 | overflow-y: scroll; 19 | background: $user-list-bgc; 20 | } 21 | } 22 | } 23 | 24 | .user-info { 25 | position: absolute; 26 | right: 0; 27 | width: $user-info-w; 28 | height: 100%; 29 | padding: 15px; 30 | background: #eee; 31 | } -------------------------------------------------------------------------------- /src/styles/index.css: -------------------------------------------------------------------------------- 1 | @charset 'UTF-8'; 2 | 3 | /*公共库*/ 4 | @import './libs/antd.css'; 5 | 6 | /*设置全局样式*/ 7 | html, body, .main { 8 | width: 100%; 9 | height: 100%; 10 | color: #58666e; 11 | } 12 | 13 | /*主题颜色*/ 14 | $dark-bgc: #1c2b36; 15 | $dark-ftc: #7793a7; 16 | $light-bgc: #edf1f2; 17 | $nav-ftc: #869fb1; 18 | $nav-hover-bgc: #16232d; 19 | $nav-active-bgc: #131e26; 20 | $proj-breadcrumb-h: 50px; 21 | $pf-toolbar-h: 60px; /*导航栏的高度*/ 22 | 23 | /*自定义公共组件*/ 24 | @import './common/index.css'; 25 | 26 | /*容器*/ 27 | @import './containers/app.css'; 28 | 29 | /*设置滚动条样式*/ 30 | ::-webkit-scrollbar { 31 | width: 3px; 32 | } 33 | 34 | ::-webkit-scrollbar-thumb:vertical { 35 | background-color: #bbb; 36 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var fs = require('fs'); 3 | 4 | var port = 3001; 5 | 6 | 7 | http.createServer(function (req, res) { 8 | console.log('access: ' + req.url); 9 | 10 | // will get you '/' or 'index.html' or 'css/styles.css' ... 11 | // • you need to isolate extension 12 | // • have a small mimetype lookup array/object 13 | // • only there and then reading the file 14 | // • delivering it after setting the right content type 15 | fs.readFile('./html/index.html', 'utf8', function(err, data) { 16 | res.writeHead(200, {'Content-Type': 'text/html'}); 17 | res.end(data); 18 | }); 19 | 20 | }).listen(port); 21 | 22 | console.log("listening on port " + port) -------------------------------------------------------------------------------- /src/scripts/components/common/users/UserInfoDialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import Dialog from 'common/ui/Dialog'; 5 | import UserInfo from './UserInfo'; 6 | 7 | let { Button, Form } = Antd; 8 | 9 | 10 | export default class UserInfoDialog extends React.Component { 11 | 12 | static defaultProps = { 13 | 14 | } 15 | 16 | state = { 17 | 18 | } 19 | 20 | constructor() { 21 | super(); 22 | } 23 | 24 | render() { 25 | let UserInfoForm = Form.create({})(UserInfo); 26 | 27 | return ( 28 | 29 | 30 | 31 | ); 32 | } 33 | 34 | componentDidMount() { 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/scripts/components/common/resources/ResourceList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Table } = Antd; 5 | 6 | 7 | export default class ResourceList extends React.Component { 8 | 9 | static defaultProps = { 10 | columns: [], 11 | data: [], 12 | rowSelection: {} 13 | } 14 | 15 | state = { 16 | 17 | } 18 | 19 | constructor() { 20 | super(); 21 | } 22 | 23 | render() { 24 | let columns = this.props.columns, 25 | rowSelection = this.props.rowSelection, 26 | data = this.props.data; 27 | return ( 28 | 29 | ); 30 | } 31 | 32 | componentDidMount() { 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/scripts/reducers/sysmanage/SysProjects.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | 3 | const initialState = { 4 | projects: [] 5 | }; 6 | 7 | const Reducers = { 8 | 9 | // 获取项目列表 10 | [act.SYS_FETCH_PROJECTS] (state, action) { 11 | return Object.assign({}, state, { 12 | projects: action.projects 13 | }) 14 | } 15 | 16 | }; 17 | 18 | /** 19 | * 根据 actionType 调用对应的 reducer 20 | * @param {[type]} state [description] 21 | * @param {[type]} action [description] 22 | * @return {[type]} [description] 23 | */ 24 | export default function SysProjects (state = initialState, action) { 25 | let reducer = Reducers[action.type]; 26 | if (reducer) { 27 | return reducer(state, action); 28 | } 29 | else { 30 | return state; 31 | } 32 | } -------------------------------------------------------------------------------- /webpack.server.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require('./webpack.dev.js'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var webpack = require('webpack'); 4 | var open = require('open'); 5 | 6 | var serverPath = 'http://127.0.0.1:3001'; 7 | var webpackServer = 'http://127.0.0.1:3000/'; 8 | 9 | new WebpackDevServer(webpack(webpackConfig), { 10 | // contentBase: serverPath, 11 | publicPath: webpackConfig.output.publicPath, 12 | hot: true, 13 | noInfo: false, 14 | historyApiFallback: true, 15 | proxy: { 16 | "\/$|\/page\/?|\/page\/.*|\/api\/.*": serverPath 17 | } 18 | }).listen(3000, 'localhost', function(err) { 19 | if (err) console.log(err); 20 | console.log('Listening at localhost:' + webpackServer); 21 | console.log('Opening your system browser...'); 22 | open(webpackServer); 23 | }); -------------------------------------------------------------------------------- /src/scripts/components/myprojects/MyProjects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import actions from 'actions' 5 | import ProjectList from 'components/common/ProjectList'; 6 | 7 | 8 | class MyProjects extends React.Component { 9 | 10 | static mapToRedux = { 11 | mapStateToProps: state => ({ 12 | projects: state.MyIndex.projects 13 | }), 14 | mapDispatchToProps: { 15 | fetchProjects: actions.MyIndex.fetchProjects 16 | } 17 | }; 18 | 19 | static defaultProps = { 20 | projects: [] 21 | } 22 | 23 | constructor() { 24 | super(); 25 | } 26 | 27 | render() { 28 | const { projects } = this.props; 29 | return ( 30 | 31 | ); 32 | } 33 | 34 | componentDidMount() { 35 | // 执行获取项目列表的 action 36 | this.props.fetchProjects(); 37 | } 38 | 39 | } 40 | 41 | export default ReduxConnect(MyProjects); -------------------------------------------------------------------------------- /src/scripts/components/sysmanage/SysProjects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import actions from 'actions' 5 | import ProjectList from 'components/common/ProjectList'; 6 | 7 | 8 | class SysProjects extends React.Component { 9 | 10 | static mapToRedux = { 11 | mapStateToProps: state => ({ 12 | projects: state.SysProjects.projects 13 | }), 14 | mapDispatchToProps: { 15 | fetchProjects: actions.SysProjects.fetchProjects 16 | } 17 | }; 18 | 19 | static defaultProps = { 20 | projects: [] 21 | } 22 | 23 | constructor() { 24 | super(); 25 | } 26 | 27 | render() { 28 | const { projects } = this.props; 29 | 30 | return ( 31 | 32 | ); 33 | } 34 | 35 | componentDidMount() { 36 | // 执行获取项目列表的 action 37 | this.props.fetchProjects(); 38 | } 39 | 40 | } 41 | 42 | export default ReduxConnect(SysProjects); -------------------------------------------------------------------------------- /src/scripts/actions/myprojects/MyIndex.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | import ProjectService from 'services/ProjectService'; 3 | 4 | /** 5 | * 我的项目 - 我的项目列表 6 | * @param {[type]} curProjectId 当前选中的项目 ID 7 | * @return {[type]} [description] 8 | */ 9 | export function switchProject (curProjectId) { 10 | return { 11 | type: act.MY_SWITCH_PROJECT, 12 | curProjectId 13 | }; 14 | } 15 | 16 | /** 17 | * 我的项目 - 获取项目列表 18 | * @return {[type]} [description] 19 | */ 20 | export function fetchProjects () { 21 | return (dispatch) => { 22 | ProjectService.getProjects().then(projects => { 23 | dispatch({ 24 | type: act.MY_FETCH_PROJECTS, 25 | projects 26 | }); 27 | }); 28 | }; 29 | } 30 | 31 | /** 32 | * 我的项目 - 切换导航 33 | * @param {[type]} curNavId 当前点击的导航的 ID 34 | * @return {[type]} [description] 35 | */ 36 | export function switchNavItem (curNavId) { 37 | return { 38 | type: act.MY_SWITCH_NAV, 39 | curNavId 40 | }; 41 | } -------------------------------------------------------------------------------- /src/scripts/components/common/users/UserInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Button, Form, Input } = Antd; 5 | const FormItem = Form.Item; 6 | 7 | 8 | export default class UserInfo extends React.Component { 9 | 10 | static defaultProps = { 11 | 12 | } 13 | 14 | state = { 15 | 16 | } 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | render() { 23 | let { getFieldProps } = this.props.form; 24 | 25 | let formItemLayout = { 26 | labelCol: { span: 7 }, 27 | wrapperCol: { span: 12 } 28 | }; 29 | 30 | return ( 31 |
32 | 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | componentDidMount() { 43 | 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/scripts/components/sysmanage/SysIndex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import actions from 'actions'; 5 | import NavBar from 'components/common/NavBar'; 6 | const { Icon } = Antd; 7 | const { Link } = ReactRouter; 8 | 9 | 10 | class SysIndex extends React.Component { 11 | 12 | static mapToRedux = { 13 | mapStateToProps: state => ({ 14 | curNavId: state.SysIndex.curNavId, 15 | navList: state.SysIndex.navList 16 | }), 17 | mapDispatchToProps: { 18 | switchNavItem: actions.SysIndex.switchNavItem 19 | } 20 | }; 21 | 22 | static defaultProps = { 23 | curNavId: '', 24 | navList: [] 25 | } 26 | 27 | constructor() { 28 | super(); 29 | } 30 | 31 | render() { 32 | const { curNavId, navList, switchNavItem } = this.props; 33 | return ( 34 |
35 | 38 | { this.props.children } 39 |
40 | ); 41 | } 42 | 43 | componentDidMount() { 44 | } 45 | 46 | } 47 | 48 | export default ReduxConnect(SysIndex); -------------------------------------------------------------------------------- /src/scripts/components/common/ProjectCreate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 新建项目对话框 3 | */ 4 | let { Modal, Button } = Antd; 5 | 6 | 7 | export default class ProjectCreate extends React.Component { 8 | 9 | static defaultProps = { 10 | visible: false, 11 | onSubmit: () => {}, 12 | onCancel: () => {}, 13 | loading: false 14 | } 15 | 16 | constructor() { 17 | super(); 18 | } 19 | 20 | render() { 21 | const { handleOk, handleCancel } = this; 22 | const { visible, loading } = this.props; 23 | 24 | return ( 25 | 取消, 29 | 30 | ]} 31 | > 32 |
新建项目
33 |
34 | ); 35 | } 36 | 37 | componentDidMount() { 38 | } 39 | 40 | handleOk() { 41 | console.log('ok'); 42 | } 43 | 44 | handleCancel() { 45 | console.log('cancel'); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | qunar.common 7 | qunar-supom-qzz 8 | 1.3.2 9 | 10 | 11 | com.qunar.qzz 12 | 13 | ***** 14 | 15 | 1.0.0-SNAPSHOT 16 | pom 17 | 18 | 19 | 20 | ver 21 | 22 | 23 | 24 | 25 | 26 | com.github.goldin 27 | copy-maven-plugin 28 | 29 | 30 | org.codehaus.mojo 31 | build-helper-maven-plugin 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/styles/components/projects/projects.css: -------------------------------------------------------------------------------- 1 | $proj-toolbar: 60px; 2 | $proj-item-icon-size: 26px; 3 | 4 | .proj-wrap { 5 | flex: 1; 6 | display: flex; 7 | flex-direction: column; 8 | 9 | .proj-list-wrap { 10 | flex: 1; 11 | display: flex; 12 | flex-direction: column; 13 | } 14 | 15 | .proj-list { 16 | flex: 1; 17 | background: #eaeaea; 18 | border-right: 1px solid #dee5e7; 19 | padding: 10px 0 0 10px; 20 | 21 | .proj-item { 22 | display: inline-block; 23 | width: 260px; 24 | margin: 10px; 25 | } 26 | 27 | .proj-item-ops { 28 | display: inline-block; 29 | overflow: hidden; 30 | .anticon { 31 | width: $proj-item-icon-size; 32 | height: $proj-item-icon-size; 33 | line-height: $proj-item-icon-size; 34 | cursor: pointer; 35 | transition: all 0.3s; 36 | 37 | &:hover { 38 | font-size: 16px; 39 | font-weight: bold; 40 | } 41 | } 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/scripts/components/common/users/UserList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import DataTable from 'common/ui/DataTable'; 5 | 6 | 7 | const columns = [ 8 | { 9 | title: '登录账号', 10 | dataIndex: 'username', 11 | key: 'username', 12 | // render: name => `${name.first} ${name.last}`, 13 | width: '20%' 14 | }, 15 | { 16 | title: '姓名', 17 | sorter: true, 18 | dataIndex: 'name', 19 | key: 'name', 20 | width: '20%' 21 | }, 22 | { 23 | title: '状态', 24 | dataIndex: 'status', 25 | key: 'status' 26 | }, 27 | { 28 | title: '创建时间', 29 | dataIndex: 'createTime', 30 | key: 'createTime' 31 | } 32 | ]; 33 | 34 | 35 | export default class UserList extends React.Component { 36 | 37 | defaultProps = { 38 | 39 | } 40 | 41 | state = { 42 | 43 | } 44 | 45 | constructor() { 46 | super(); 47 | } 48 | 49 | render() { 50 | return ( 51 |
52 | 53 |
54 | ); 55 | } 56 | 57 | componentDidMount() { 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /src/scripts/components/common/users/Users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Button, Icon } = Antd; 5 | 6 | import UserList from './UserList'; 7 | import UserInfoDialog from './UserInfoDialog'; 8 | 9 | 10 | export default class Users extends React.Component { 11 | 12 | defaultProps = { 13 | 14 | } 15 | 16 | state = { 17 | userInfoVisible: false 18 | } 19 | 20 | constructor() { 21 | super(); 22 | } 23 | 24 | render() { 25 | return ( 26 |
27 |
28 | 31 |
32 |
33 |
34 | 35 |
36 |
37 | 41 |
42 | ); 43 | } 44 | 45 | componentDidMount() { 46 | 47 | } 48 | 49 | btnAddUserClick() { 50 | console.log('22222'); 51 | } 52 | 53 | onUserInfoOkClick() { 54 | 55 | } 56 | 57 | onUserInfoCanelClick() { 58 | 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /webpack.dll.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var CleanWebpackPlugin = require('clean-webpack-plugin'); 3 | var ProgressPlugin = require('webpack/lib/ProgressPlugin'); 4 | var WebpackMd5Hash = require('webpack-md5-hash'); 5 | var path = require('path'); 6 | 7 | 8 | module.exports = { 9 | 10 | entry: { 11 | vendors: [ 12 | 'isomorphic-fetch', 13 | 'react', 14 | 'react-dom', 15 | 'react-router', 16 | 'redux', 17 | 'redux-thunk', 18 | 'react-redux', 19 | 'redux-promise-middleware', 20 | 'antd' 21 | ] 22 | }, 23 | 24 | output: { 25 | path: path.resolve('./dll/'), 26 | filename: '[name]@[chunkhash].js', 27 | library: '[name]_[chunkhash]' 28 | }, 29 | 30 | plugins: [ 31 | new CleanWebpackPlugin(['dll']), 32 | new webpack.DllPlugin({ 33 | path: path.resolve('./dll/[name]_manifest.json'), 34 | name: '[name]_[chunkhash]', 35 | context: __dirname 36 | }), 37 | // 压缩 js 38 | // new webpack.optimize.UglifyJsPlugin({ 39 | // compress: { 40 | // warnings: false 41 | // } 42 | // }), 43 | // 根据文件内容生成 MD5 44 | new WebpackMd5Hash(), 45 | // 显示进度 46 | new ProgressPlugin(function(percentage, msg) { 47 | console.log(parseInt(percentage * 100) + '%', msg); 48 | }) 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /src/scripts/common/ui/ToolBar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | const { Icon } = Antd; 5 | const { Link } = ReactRouter; 6 | 7 | 8 | export default class ToolBar extends React.Component { 9 | 10 | static defaultProps = { 11 | activeKey: '', 12 | toolbarList: [], 13 | onChange: () => {} 14 | } 15 | 16 | constructor() { 17 | super(); 18 | } 19 | 20 | render() { 21 | const { activeKey, toolbarList } = this.props; 22 | let className = '', itemKey = ''; 23 | let toolbarItems = null; 24 | // 生成导航按钮 25 | if (toolbarList.length > 0) { 26 | toolbarItems = toolbarList.map( item => { 27 | itemKey = item.key; 28 | if (itemKey === activeKey) { 29 | className = 'active'; 30 | } else { 31 | className = ''; 32 | } 33 | return ( 34 |
  • 35 | 36 | 37 | {item.title} 38 | 39 |
  • 40 | ); 41 | }); 42 | } 43 | 44 | return ( 45 | 50 | ); 51 | } 52 | 53 | componentDidMount() { 54 | } 55 | 56 | itemClick(itemKey) { 57 | this.props.onChange(itemKey); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/styles/common/toolbar.css: -------------------------------------------------------------------------------- 1 | .pf-toolbar { 2 | height: $pf-toolbar-h; 3 | background: $dark-bgc; 4 | 5 | .pf-tlb-nav { 6 | line-height: $pf-toolbar-h; 7 | 8 | a { 9 | color: inherit; 10 | } 11 | 12 | .nav-item { 13 | display: inline-block; 14 | height: $pf-toolbar-h; 15 | padding: 0 15px; 16 | color: $nav-ftc; 17 | cursor: pointer; 18 | 19 | .icon { 20 | font-size: 16px; 21 | display: block; 22 | line-height: normal; 23 | padding: 10px; 24 | opacity: .7; 25 | transition: all .3s; 26 | 27 | &.users { color: #457fd6; } 28 | &.roles { color: #c5a42b; } 29 | &.resources { color: #8a62da; } 30 | &.projects { color: #4bbb54; } 31 | } 32 | 33 | .text { 34 | display: block; 35 | line-height: normal; 36 | text-align: center; 37 | color: $nav-ftc; 38 | transition: all .3s; 39 | } 40 | 41 | &.active { 42 | background: $nav-active-bgc; 43 | .icon { opacity: 1; } 44 | .text { color: #fff; } 45 | } 46 | 47 | &:hover { 48 | background: $nav-hover-bgc; 49 | .icon { opacity: 1; } 50 | .text { color: #fff; } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/scripts/common/ui/DropSelect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | const { Icon, Menu, Dropdown } = Antd; 5 | const { Link } = ReactRouter; 6 | 7 | export default class DropSelect extends React.Component { 8 | 9 | static defaultProps = { 10 | title: '请选择项目 V', 11 | selectedKey: '', 12 | items: [], 13 | onSelect: () => {} 14 | } 15 | 16 | constructor() { 17 | super(); 18 | } 19 | 20 | render() { 21 | const { title, items, selectedKey, onSelect } = this.props; 22 | let selectItem = searchItem(selectedKey, items); 23 | let showTitle = title; 24 | if (selectItem) { 25 | showTitle = selectItem.title; 26 | } 27 | 28 | const menu = ( 29 | 30 | { 31 | items.map(item => { 32 | return ( 33 | 34 | {item.title} 35 | 36 | ); 37 | }) 38 | } 39 | 40 | ); 41 | 42 | return ( 43 | document.querySelector('.navbar-title-wrap')}> 44 | 45 | {showTitle} 46 | 47 | 48 | ); 49 | } 50 | 51 | componentDidMount() { 52 | } 53 | 54 | } 55 | 56 | 57 | function searchItem (key, items) { 58 | for (var i = 0; i < items.length; i++) { 59 | if(items[i].key === key) { 60 | return items[i]; 61 | } 62 | } 63 | return null; 64 | } -------------------------------------------------------------------------------- /src/scripts/components/myprojects/MyIndex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import actions from 'actions'; 5 | 6 | import MyProjects from 'components/myprojects/MyProjects'; 7 | import NavBar from 'components/common/NavBar'; 8 | let { Button, Icon, Card } = Antd; 9 | 10 | 11 | class MyIndex extends React.Component { 12 | 13 | static mapToRedux = { 14 | mapStateToProps: state => ({ 15 | curProjectId: state.MyIndex.curProjectId, 16 | curNavId: state.MyIndex.curNavId, 17 | projects: state.MyIndex.projects, 18 | navList: state.MyIndex.navList 19 | }), 20 | mapDispatchToProps: { 21 | fetchProjects: actions.MyIndex.fetchProjects, 22 | switchNavItem: actions.MyIndex.switchNavItem, 23 | switchProject: actions.MyIndex.switchProject 24 | } 25 | }; 26 | 27 | static defaultProps = { 28 | curProjectId: '', 29 | projects: [], 30 | navList: [], 31 | curNavId: '', 32 | switchProject: () => {} 33 | } 34 | 35 | constructor() { 36 | super(); 37 | } 38 | 39 | render() { 40 | const { projects, curProjectId, navList, curNavId, switchNavItem } = this.props; 41 | return ( 42 |
    43 | this.projectSelect(args)} 47 | /> 48 | { this.props.children || } 49 |
    50 | ); 51 | } 52 | 53 | componentDidMount() { 54 | } 55 | 56 | projectSelect(params) { 57 | this.props.switchProject(params.key); 58 | } 59 | 60 | } 61 | 62 | export default ReduxConnect(MyIndex); -------------------------------------------------------------------------------- /src/scripts/reducers/sysmanage/SysIndex.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | import * as links from 'constants/Links'; 3 | 4 | const initialState = { 5 | projectId: 'system', 6 | curNavId: 'sys-nav-1', 7 | navList: [ 8 | { 9 | key: 'sys-nav-1', 10 | title: '项目管理', 11 | link: links.SYS_PROJECTS, 12 | icon: 'projects', 13 | iconType: 'github' 14 | }, 15 | { 16 | key: 'sys-nav-2', 17 | title: '用户管理', 18 | link: links.SYS_USERS, 19 | icon: 'users', 20 | iconType: 'team' 21 | }, 22 | { 23 | key: 'sys-nav-3', 24 | title: '角色管理', 25 | link: links.SYS_ROLES, 26 | icon: 'roles', 27 | iconType: 'solution' 28 | }, 29 | { 30 | key: 'sys-nav-4', 31 | title: '资源管理', 32 | link: links.SYS_RESOURCES, 33 | icon: 'resources', 34 | iconType: 'appstore' 35 | } 36 | ] 37 | }; 38 | 39 | const Reducers = { 40 | 41 | // 获取项目列表 42 | [act.SYS_FETCH_PROJECTS] (state, action) { 43 | return Object.assign({}, state, { 44 | projects: action.projects 45 | }) 46 | }, 47 | 48 | // 系统管理切换导航 49 | [act.SYS_SWITCH_NAV] (state, action) { 50 | return Object.assign({}, state, { 51 | curNavId: action.curNavId || initialState.curNavId 52 | }) 53 | } 54 | 55 | }; 56 | 57 | /** 58 | * 根据 actionType 调用对应的 reducer 59 | * @param {[type]} state [description] 60 | * @param {[type]} action [description] 61 | * @return {[type]} [description] 62 | */ 63 | export default function SysIndex (state = initialState, action) { 64 | let reducer = Reducers[action.type]; 65 | if (reducer) { 66 | return reducer(state, action); 67 | } 68 | else { 69 | return state; 70 | } 71 | } -------------------------------------------------------------------------------- /src/scripts/AppRouter.js: -------------------------------------------------------------------------------- 1 | // 配置路由 2 | let { Router, Route, browserHistory, IndexRoute } = ReactRouter; 3 | 4 | // 引入页面模块 5 | import App from './containers/App'; 6 | import SysIndex from './components/sysmanage/SysIndex'; 7 | import SysProjects from './components/sysmanage/SysProjects'; 8 | 9 | import MyIndex from './components/myprojects/MyIndex'; 10 | import MyProjects from './components/myprojects/MyProjects'; 11 | 12 | import Users from './components/common/users/Users'; 13 | import Roles from './components/common/roles/Roles'; 14 | import Resources from './components/common/resources/Resources'; 15 | 16 | 17 | export default class AppRouter extends React.Component { 18 | 19 | constructor() { 20 | super(); 21 | } 22 | 23 | render() { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /src/scripts/components/header/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 页面的头部,包括 3 | * logo 4 | * 基本操作栏 5 | */ 6 | import * as links from 'constants/Links'; 7 | import actions from 'actions'; 8 | 9 | const { Icon } = Antd; 10 | const { Link } = ReactRouter; 11 | 12 | 13 | class Header extends React.Component { 14 | 15 | static mapToRedux = { 16 | mapStateToProps: state => ({ 17 | 18 | }), 19 | mapDispatchToProps: { 20 | switchProject: actions.MyIndex.switchProject, 21 | switchNavItem: actions.SysIndex.switchNavItem 22 | } 23 | }; 24 | 25 | constructor() { 26 | super() 27 | } 28 | 29 | render() { 30 | const username = window.username; 31 | 32 | return ( 33 |
    34 |
    35 |
    36 |

    UPMS

    37 |
    38 |
    39 | 你好, {username} ! 40 | this.clickMyProjects()}> 41 | 我的项目 42 | 43 | this.clickSysManage()}> 44 | 系统管理 45 | 46 | 退出 47 |
    48 |
    49 |
    50 | ); 51 | } 52 | 53 | componentDidMount() { 54 | } 55 | 56 | clickMyProjects() { 57 | this.props.switchProject(''); 58 | } 59 | 60 | clickSysManage() { 61 | this.props.switchNavItem(''); 62 | } 63 | 64 | } 65 | 66 | export default ReduxConnect(Header); -------------------------------------------------------------------------------- /src/scripts/components/common/ProjectList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Button, Icon, Card } = Antd; 5 | import ProjectCreate from 'components/common/ProjectCreate'; 6 | 7 | 8 | export default class ProjectList extends React.Component { 9 | 10 | static defaultProps = { 11 | projects: [], 12 | projCreateVisible: false 13 | } 14 | 15 | constructor() { 16 | super(); 17 | } 18 | 19 | render() { 20 | const { projects, projCreateVisible } = this.props; 21 | let projectsOperate = null; 22 | // 生成项目卡片列表 23 | let projectCardList = projects.map( item => { 24 | // 卡片上的操作按钮 25 | projectsOperate = ( 26 | 27 | 28 | 29 | 30 | ); 31 | // 单个卡片组件 32 | return ( 33 | 36 |

    用户数量: {item.userCount}

    37 |

    角色数量: {item.roleCount}

    38 |

    资源数量: {item.resourceCount}

    39 |
    40 | ); 41 | }); 42 | // 渲染卡片列表,工具栏 43 | return ( 44 |
    45 |
    46 | 49 |
    50 |
    51 | {projectCardList} 52 |
    53 | 54 |
    55 | ); 56 | } 57 | 58 | componentDidMount() { 59 | 60 | } 61 | 62 | showAddProject() { 63 | 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/scripts/common/utils/ReduxConnect.js: -------------------------------------------------------------------------------- 1 | const { connect } = ReactRedux; 2 | const { bindActionCreators } = Redux; 3 | 4 | /** 5 | * 生成 mapDispatchToProps 对象 6 | * 使用 bindActionCreators 方法封装 actionCreator 7 | * @param {[type]} propsMap [description] 8 | * @param {[type]} dispatch [description] 9 | */ 10 | function setMapDispatchToProps (dispatch, propsMap, ClassDefine) { 11 | let mapDispatchToProps = {}, key = '', item = null; 12 | for(key in propsMap) { 13 | item = propsMap[key]; 14 | if (!item) { 15 | console.error(`mapDispatchToProps.${key} is undefined! please check your component Class: ${ClassDefine.name}`); 16 | } 17 | mapDispatchToProps[key] = bindActionCreators(item, dispatch); 18 | } 19 | return mapDispatchToProps; 20 | } 21 | 22 | /** 23 | * 封装 connect 方法, 在组件内以更简洁的方式调用 stateToProps 和 dispatchToProps 24 | * @param {[type]} ClassDefine [description] 25 | * @return {[type]} [description] 26 | */ 27 | module.exports = function ReduxConnect (ClassDefine) { 28 | if (typeof ClassDefine === 'function' && 29 | typeof ClassDefine.mapToRedux === 'object' && 30 | typeof ClassDefine.mapToRedux.mapStateToProps === 'function' && 31 | typeof ClassDefine.mapToRedux.mapDispatchToProps === 'object' 32 | ) { 33 | const mapToRedux = ClassDefine.mapToRedux; 34 | return connect( 35 | mapToRedux.mapStateToProps, 36 | (dispatch) => setMapDispatchToProps(dispatch, mapToRedux.mapDispatchToProps, ClassDefine) 37 | )(ClassDefine); 38 | } 39 | else { 40 | console.error( 41 | `ReduxConnect can not connect this Class, 42 | because the Class.mapToRedux is invalid, 43 | please make sure Class.mapToRedux.mapStateToProps is a function, 44 | ClassDefine.mapToRedux.mapDispatchToProps is a object` 45 | ); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/styles/components/header/header.css: -------------------------------------------------------------------------------- 1 | $topbar-h: 35px; 2 | $navbar-h: 60px; 3 | $ops-btn-color: #2db7f5; 4 | 5 | .pf-head { 6 | height: $topbar-h; 7 | background: $dark-bgc; 8 | color: $dark-ftc; 9 | z-index: 10; 10 | 11 | .pf-topbar { 12 | height: $topbar-h; 13 | background: $light-bgc; 14 | display: flex; 15 | 16 | .pf-logo { 17 | display: inline-block; 18 | height: $topbar-h; 19 | overflow: hidden; 20 | width: 120px; 21 | color: #1c2b36; 22 | 23 | h1 { 24 | font-size: 37px; 25 | margin-top: 5px; 26 | line-height: 35px; 27 | text-align: center; 28 | text-shadow: 0 0 3px #131e26; 29 | } 30 | } 31 | 32 | .pf-switch-proj { 33 | display: inline-block; 34 | // width: 260px; 35 | line-height: $topbar-h; 36 | margin-left: 20px; 37 | .pf-tlb-text { 38 | display: inline-block; 39 | vertical-align: middle; 40 | } 41 | .ant-select { 42 | width: 200px; 43 | } 44 | } 45 | 46 | .pf-tlb-info { 47 | display: inline-block; 48 | line-height: $topbar-h; 49 | text-align: right; 50 | flex: 1; 51 | 52 | .pf-tlb-tips { 53 | display: inline-block; 54 | padding: 0 5px; 55 | } 56 | .pf-tlb-button { 57 | padding: 0 10px; 58 | cursor: pointer; 59 | .anticon { 60 | color: $ops-btn-color; 61 | } 62 | &:hover { 63 | color: $ops-btn-color; 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | .pf-body { 71 | flex: 1; 72 | background: $light-bgc; 73 | } -------------------------------------------------------------------------------- /src/scripts/common/ui/DataTable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Table } = Antd; 5 | 6 | 7 | export default class DataTable extends React.Component { 8 | 9 | defaultProps = { 10 | columns: [] 11 | } 12 | 13 | state = { 14 | data: [], 15 | pagination: {}, 16 | loading: false 17 | } 18 | 19 | constructor() { 20 | super(); 21 | } 22 | 23 | render() { 24 | return ( 25 |
    31 | ); 32 | } 33 | 34 | componentDidMount() { 35 | this.fetch(); 36 | } 37 | 38 | fetch() { 39 | let pagination = this.state.pagination; 40 | // Read total count from server 41 | // pagination.total = data.totalCount; 42 | let data = { 43 | results: [ 44 | { 45 | key: 1, 46 | username: '123', 47 | name: '123', 48 | status: true, 49 | createTime: '123' 50 | } 51 | ] 52 | } 53 | pagination.total = 200; 54 | this.setState({ 55 | loading: false, 56 | data: data.results, 57 | pagination 58 | }); 59 | } 60 | 61 | handleTableChange(pagination, filters, sorter) { 62 | console.log(pagination); 63 | let pager = this.state.pagination; 64 | pager.current = pagination.current; 65 | this.setState({ 66 | pagination: pager 67 | }); 68 | this.fetch({ 69 | results: pagination.pageSize, 70 | page: pagination.current, 71 | sortField: sorter.field, 72 | sortOrder: sorter.order, 73 | ...filters 74 | }); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/scripts/common/ui/Dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | let { Modal, Button } = Antd; 5 | 6 | 7 | export default class Dialog extends React.Component { 8 | 9 | static defaultProps = { 10 | title: "用户信息", 11 | okBtnText: '提 交', 12 | cancelBtnText: '返 回', 13 | visible: false, 14 | loading: false, 15 | maskClosable: false, 16 | onOkClick: () => {}, 17 | onCancelClick: null 18 | } 19 | 20 | state = { 21 | visible: false 22 | } 23 | 24 | constructor() { 25 | super(); 26 | // 标记属性更新是否通过 state 执行的 27 | this.fromState = false; 28 | } 29 | 30 | render() { 31 | let handleOk = this.props.onOkClick, 32 | handleCancel = this.props.onCancelClick || this.onCancelClick.bind(this), 33 | loading = this.props.loading, 34 | visible = this.props.visible; 35 | 36 | // 如果使用 setState 方式更新 UI 则取 state 中的值 37 | // 默认从 props 中获取 38 | if (this.fromState) { 39 | visible = this.state.visible; 40 | this.fromState = false; 41 | } 42 | 43 | return ( 44 | 49 | {this.props.cancelBtnText} 50 | , 51 | 54 | ]} 55 | > 56 | { this.props.children } 57 | 58 | ); 59 | } 60 | 61 | componentDidMount() { 62 | 63 | } 64 | 65 | onCancelClick() { 66 | this.fromState = true; 67 | this.setState({ 68 | visible: false 69 | }); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/styles/components/header/navbar.css: -------------------------------------------------------------------------------- 1 | .navbar-wrap { 2 | display: flex; 3 | background: $dark-bgc; 4 | 5 | .nabr-item { 6 | display: inline-block; 7 | } 8 | 9 | .navbar-left { 10 | width: 120px; 11 | line-height: $pf-toolbar-h; 12 | text-align: center; 13 | color: #fff; 14 | 15 | .navbar-title-wrap { 16 | display: inline-block; 17 | vertical-align: top; 18 | height: $pf-toolbar-h; 19 | line-height: $pf-toolbar-h; 20 | 21 | .ant-dropdown-menu { 22 | background: $dark-bgc; 23 | 24 | .ant-dropdown-menu-item { 25 | color: $nav-ftc; 26 | 27 | &:hover { 28 | background: $nav-hover-bgc; 29 | color: #fff; 30 | } 31 | } 32 | } 33 | } 34 | 35 | .navbar-title { 36 | color: #fff; 37 | } 38 | 39 | .ant-dropdown-link { 40 | display: inline-block; 41 | width: 97px; 42 | white-space: nowrap; 43 | overflow: hidden; 44 | text-overflow: ellipsis; 45 | } 46 | 47 | .anticon.anticon-down { 48 | margin-left: 10px; 49 | vertical-align: top; 50 | line-height: $pf-toolbar-h; 51 | } 52 | } 53 | 54 | .navbar-arr { 55 | width: 35px; 56 | height: $pf-toolbar-h; 57 | overflow: hidden; 58 | 59 | .box-arrow-scale { 60 | transform: scale(0.5, 1); 61 | .box-arrow { 62 | width: 100px; 63 | height: 100px; 64 | transform: rotateZ(45deg) translate3d(-81px, 52px, 0); 65 | background: $dark-bgc; 66 | box-shadow: 2px 0px 10px #000; 67 | } 68 | } 69 | } 70 | 71 | .navbar-right { 72 | flex: 1; 73 | } 74 | 75 | .breadcrumb-arrow .anticon { 76 | opacity: 0.5; 77 | transform: scale(0.8); 78 | } 79 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dzs-upms", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "init": "npm install --registry https://registry.npm.taobao.org", 7 | "dev": "node webpack.server.js", 8 | "publish": "webpack --config webpack.prod.js --colors --profile --display-modules", 9 | "dll": "webpack --config webpack.dll.js --colors --profile --display-modules" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git@github.com:functions/webpack-react.git" 14 | }, 15 | "jshintConfig": { 16 | "esnext": true 17 | }, 18 | "author": "functions", 19 | "license": "ISC", 20 | "dependencies": { 21 | "antd": "^1.10.0", 22 | "isomorphic-fetch": "^2.2.1", 23 | "promise": "^7.1.1", 24 | "react": "^15.3.1", 25 | "react-dom": "^15.3.1", 26 | "react-redux": "^4.4.5", 27 | "react-router": "^2.0.1", 28 | "redux": "^3.5.2", 29 | "redux-logger": "^2.6.1", 30 | "redux-promise-middleware": "^4.0.0", 31 | "redux-thunk": "^2.1.0" 32 | }, 33 | "devDependencies": { 34 | "autoprefixer": "^6.3.7", 35 | "babel-core": "^6.7.2", 36 | "babel-loader": "^6.2.4", 37 | "babel-preset-es2015": "^6.6.0", 38 | "babel-preset-react": "^6.5.0", 39 | "babel-preset-stage-0": "^6.5.0", 40 | "clean-webpack-plugin": "^0.1.10", 41 | "copy-webpack-plugin": "^3.0.1", 42 | "css-loader": "^0.23.1", 43 | "css-mqpacker": "^5.0.1", 44 | "del": "^2.2.1", 45 | "extract-text-webpack-plugin": "^1.0.1", 46 | "file-loader": "^0.8.5", 47 | "html-webpack-plugin": "^2.22.0", 48 | "json-loader": "^0.5.4", 49 | "jsx-loader": "^0.13.2", 50 | "open": "0.0.5", 51 | "postcss-assets": "^4.1.0", 52 | "postcss-cssnext": "^2.7.0", 53 | "postcss-font-grabber": "^1.0.7", 54 | "postcss-import": "^8.1.2", 55 | "postcss-loader": "^0.9.1", 56 | "postcss-scss": "^0.1.9", 57 | "postcss-strip-inline-comments": "^0.1.5", 58 | "precss": "^1.4.0", 59 | "react-hot-loader": "^1.3.0", 60 | "style-loader": "^0.13.1", 61 | "url-loader": "^0.5.7", 62 | "webpack": "^1.12.14", 63 | "webpack-dev-server": "^1.14.1", 64 | "webpack-md5-hash": "0.0.5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/scripts/components/common/NavBar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import DropSelect from 'common/ui/DropSelect'; 5 | import ToolBar from 'common/ui/ToolBar'; 6 | const { Icon } = Antd; 7 | 8 | 9 | export default class NavBar extends React.Component { 10 | 11 | static defaultProps = { 12 | type: 'DROPDOWN', 13 | // dropdown 模式下的属性 14 | dropSelectData: [], 15 | activeSelect: '', 16 | onSelect: () => {}, 17 | // 导航相关的属性和事件 18 | navItems: [], 19 | activeNav: '', 20 | onNavChange: () => {}, // 导航切换的事件处理 21 | // 标题模式下的属性 22 | title: '', 23 | icon: '' 24 | } 25 | 26 | constructor() { 27 | super(); 28 | } 29 | 30 | render() { 31 | const { type, dropSelectData, onSelect, activeSelect, 32 | navItems, activeNav, title, icon, onNavChange } = this.props; 33 | 34 | let navbarLeft = ''; 35 | switch(type){ 36 | case 'DROPDOWN': 37 | navbarLeft = ; 40 | break; 41 | case 'TITLE': 42 | navbarLeft = ( 43 | 44 | {title} 45 | 46 | 47 | ); 48 | break; 49 | default: 50 | navbarLeft = ; 53 | } 54 | 55 | return ( 56 |
    57 |
    58 | { navbarLeft } 59 |
    60 |
    61 |
    62 |
    63 |
    64 |
    65 |
    66 | 67 |
    68 |
    69 | ); 70 | } 71 | 72 | componentDidMount() { 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/scripts/components/common/projects/ProjectInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import Dialog from 'common/ui/Dialog'; 5 | let { Button, Input, Select } = Antd; 6 | let Option = Select.Option; 7 | 8 | 9 | export default class ProjectInfo extends React.Component { 10 | 11 | static defaultProps = { 12 | value: '', 13 | managers: [], 14 | onFinish: () => {} 15 | } 16 | 17 | state = { 18 | visible: false 19 | } 20 | 21 | constructor() { 22 | super(); 23 | } 24 | 25 | render() { 26 | let selectedManagers = []; 27 | let managersOption = this.props.managers.map(function(item) { 28 | if (item.selected) { 29 | selectedManagers.push(item.text); 30 | } 31 | return ( 32 | 33 | ); 34 | }); 35 | const { visible, value } = this.props; 36 | 37 | return ( 38 | this.submitProject} 40 | > 41 |
    42 | 项目名称: 43 | 44 |
    45 |
    46 | 管理员: 47 | 56 |
    57 |
    58 | ); 59 | } 60 | 61 | componentDidMount() { 62 | 63 | } 64 | 65 | show() { 66 | this.setState({ 67 | visible: true 68 | }); 69 | setTimeout(function () { 70 | this.refs.projNameInput.refs.input.focus(); 71 | }, 300); 72 | } 73 | 74 | handleManageSelect() { 75 | 76 | } 77 | 78 | submitProject() { 79 | let projectName = this.refs.projNameInput.refs.input.value; 80 | console.log(projectName); 81 | this.setState({ 82 | visible: false 83 | }); 84 | 85 | // 处理完成 86 | this.props.onFinish(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * configuration description 3 | * https://github.com/webpack/docs/wiki/configuration#configuration-object-content 4 | * webpack document 5 | * http://webpack.github.io/docs/ 6 | */ 7 | 'use strict' 8 | var webpack = require('webpack'); 9 | var ProgressPlugin = require('webpack/lib/ProgressPlugin'); 10 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 11 | var CleanWebpackPlugin = require('clean-webpack-plugin'); 12 | var WebpackMd5Hash = require('webpack-md5-hash'); 13 | 14 | // 引入基础配置 15 | var baseConfig = require('./webpack.base.js'); 16 | // 引入第三方库的 dll 文件 17 | var vendorsManifest = require('./dll/vendors_manifest.json'); 18 | 19 | //提取css独立成文件 20 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 21 | var extractSCSS = new ExtractTextPlugin('[name]@[chunkhash].css'); 22 | 23 | 24 | 25 | module.exports = { 26 | //获取项目入口js文件 27 | entry: baseConfig.entry, 28 | output: baseConfig.prodOutput, 29 | resolve: baseConfig.resolve, 30 | //各种加载器,即让各种文件格式可用require引用 31 | module: { 32 | loaders: baseConfig.module.loaders.concat( 33 | // js 类型资源的处理 34 | { 35 | test: /\.jsx?$/, 36 | include: /src\/scripts\//, 37 | loaders: ['babel-loader?presets[]=react,presets[]=es2015,presets[]=stage-0'] 38 | }, 39 | { 40 | test: /\.css$/, 41 | include: /src\/styles\//, 42 | loader: extractSCSS.extract('css-loader?sourceMap&minimize!postcss-loader?parser=postcss-scss') 43 | } 44 | ) 45 | }, 46 | plugins: [ 47 | // 清空编译后的目录 48 | new CleanWebpackPlugin(['prd']), 49 | // 引用打包好的第三方库 50 | new webpack.DllReferencePlugin({ 51 | context: __dirname, 52 | manifest: vendorsManifest 53 | }), 54 | // 把第三方库的依赖注入到代码中 55 | new webpack.ProvidePlugin(baseConfig.provideLibs), 56 | extractSCSS, 57 | //js文件的压缩 58 | new webpack.optimize.UglifyJsPlugin({ 59 | compress: { 60 | warnings: false 61 | } 62 | }), 63 | // 根据文件内容生成 MD5 64 | new WebpackMd5Hash(), 65 | new ProgressPlugin(function(percentage, msg) { 66 | console.log(parseInt(percentage * 100) + '%', msg); 67 | }) 68 | ], 69 | postcss: baseConfig.postcss 70 | }; -------------------------------------------------------------------------------- /webpack.base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * webpack 基础的配置 3 | */ 4 | var path = require('path'); 5 | 6 | 7 | module.exports = { 8 | entry: { 9 | index: './src/scripts/index.js' 10 | }, 11 | 12 | devOutput: { 13 | //文件输出目录 14 | path: path.join(__dirname, 'prd'), 15 | //用于配置文件发布路径,如CDN或本地服务器 16 | publicPath: '/prd/', 17 | //根据入口文件输出的对应多个文件名 18 | filename: '[name].js' 19 | }, 20 | 21 | prodOutput: { 22 | //文件输出目录 23 | path: path.join(__dirname, 'prd'), 24 | //用于配置文件发布路径,如CDN或本地服务器 25 | publicPath: '/prd/', 26 | //根据入口文件输出的对应多个文件名 27 | filename: '[name]@[chunkhash].js' 28 | }, 29 | 30 | resolve: { 31 | alias: { 32 | styles: path.join(__dirname, 'src', 'styles'), 33 | common: path.join(__dirname, 'src', 'scripts', 'common'), 34 | components: path.join(__dirname, 'src', 'scripts', 'components'), 35 | actions: path.join(__dirname, 'src', 'scripts', 'actions'), 36 | constants: path.join(__dirname, 'src', 'scripts', 'constants'), 37 | services: path.join(__dirname, 'src', 'scripts', 'services') 38 | }, 39 | extensions: ['', '.js', '.css'] 40 | }, 41 | 42 | module: { 43 | loaders: [ 44 | //图片文件使用 url-loader 来处理,小于8kb的直接转为base64 45 | { 46 | test: /.(png|jpg|woff(2)?|eot|ttf|svg)(\?[a-z0-9=\.]+)?$/, 47 | loader: 'url-loader?limit=50000' 48 | }, 49 | { 50 | test: /\.(png|jpg|woff(2)?|eot|ttf|svg)(\?[a-z0-9=\.]+)?$/, 51 | loader: 'file-loader' 52 | }, 53 | //.json 文件使用json-loader 来编译处理 54 | { 55 | test: /\.json$/, 56 | loader: 'json-loader' 57 | } 58 | ] 59 | }, 60 | 61 | provideLibs: { 62 | Antd: 'antd', 63 | React: 'react', 64 | ReactDOM: 'react-dom', 65 | ReactRouter: 'react-router', 66 | Redux: 'redux', 67 | ReactRedux: 'react-redux', 68 | ReduxThunk: 'redux-thunk', 69 | ReduxConnect: 'common/utils/ReduxConnect.js' 70 | }, 71 | 72 | postcss: function () { 73 | return [ 74 | require('precss'), 75 | require('postcss-strip-inline-comments'), 76 | require('postcss-cssnext')() 77 | ]; 78 | } 79 | }; -------------------------------------------------------------------------------- /src/scripts/reducers/myprojects/MyIndex.js: -------------------------------------------------------------------------------- 1 | import * as act from 'constants/ActionTypes'; 2 | import * as links from 'constants/Links'; 3 | 4 | // 默认选中项目 5 | const EMPTY_PROJ = '请选择项目'; 6 | 7 | // 单个项目的导航列表 8 | const NAV_LIST = [ 9 | { 10 | key: 'nav-2', 11 | title: '用户管理', 12 | link: links.MY_PROJECT_USERS, 13 | icon: 'users', 14 | iconType: 'team' 15 | }, 16 | { 17 | key: 'nav-3', 18 | title: '角色管理', 19 | link: links.MY_PROJECT_ROLES, 20 | icon: 'roles', 21 | iconType: 'solution' 22 | }, 23 | { 24 | key: 'nav-4', 25 | title: '资源管理', 26 | link: links.MY_PROJECT_RESOURCES, 27 | icon: 'resources', 28 | iconType: 'appstore' 29 | } 30 | ]; 31 | 32 | const initialState = { 33 | curProjectId: EMPTY_PROJ, 34 | projects: [], 35 | curNavId: 'nav-2', 36 | navList: [] 37 | }; 38 | 39 | const Reducers = { 40 | 41 | /** 42 | * 获取我的项目列表 43 | */ 44 | [act.MY_FETCH_PROJECTS] (state, action) { 45 | // 遍历每个项目的信息并为其添加 href 属性 46 | let href = ''; 47 | let projects = [].concat(action.projects).map( item => { 48 | href = links.MY_PROJECT_HREF.replace(':id', item.key); 49 | return Object.assign({}, item, { href }); 50 | }); 51 | return Object.assign({}, state, { projects }); 52 | }, 53 | 54 | /** 55 | * 切换项目 56 | */ 57 | [act.MY_SWITCH_PROJECT] (state, action) { 58 | const curProjectId = action.curProjectId || EMPTY_PROJ; 59 | let navList = []; 60 | // 如果没有选择任何项目 61 | if (curProjectId === EMPTY_PROJ) { 62 | navList = []; 63 | } 64 | // 如果选择了一个项目 65 | else { 66 | let link = ''; 67 | // 修改导航按钮的链接为当前项目的链接 68 | navList = [].concat(NAV_LIST).map( item => { 69 | link = item.link.replace(':id', curProjectId); 70 | return Object.assign({}, item, { link }); 71 | }); 72 | } 73 | // 返回切换后的结果 74 | return Object.assign({}, state, { 75 | curProjectId, 76 | navList, 77 | curNavId: initialState.curNavId 78 | }); 79 | }, 80 | 81 | /** 82 | * 切换导航 83 | */ 84 | [act.MY_SWITCH_NAV] (state, action) { 85 | return Object.assign({}, state, { 86 | curNavId: action.curNavId 87 | }) 88 | } 89 | 90 | }; 91 | 92 | /** 93 | * 根据 actionType 调用对应的 reducer 94 | * @param {[type]} state [description] 95 | * @param {[type]} action [description] 96 | * @return {[type]} [description] 97 | */ 98 | export default function MyProjects (state = initialState, action) { 99 | let reducer = Reducers[action.type]; 100 | if (reducer) { 101 | return reducer(state, action); 102 | } 103 | else { 104 | return state; 105 | } 106 | } -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * configuration description 3 | * https://github.com/webpack/docs/wiki/configuration#configuration-object-content 4 | * webpack document 5 | * http://webpack.github.io/docs/ 6 | */ 7 | 'use strict' 8 | var webpack = require('webpack'); 9 | var ProgressPlugin = require('webpack/lib/ProgressPlugin'); 10 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 11 | var CleanWebpackPlugin = require('clean-webpack-plugin'); 12 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 13 | // 引入基础配置 14 | var baseConfig = require('./webpack.base.js'); 15 | // 引入第三方库的 dll 文件 16 | var vendorsManifest = require('./dll/vendors_manifest.json'); 17 | var vendorsFileName = vendorsManifest.name.replace('_', '@') + '.js'; 18 | 19 | 20 | 21 | module.exports = { 22 | // 获取项目入口js文件 23 | entry: genEntry(), 24 | output: baseConfig.devOutput, 25 | resolve: baseConfig.resolve, 26 | // 生成sourcemap,便于开发调试 27 | devtool: "cheap-source-map", 28 | //各种加载器,即让各种文件格式可用require引用 29 | module: { 30 | loaders: baseConfig.module.loaders.concat( 31 | // js 类型资源的处理 32 | { 33 | test: /\.jsx?$/, 34 | include: /src\/scripts\//, 35 | loaders: ['react-hot', 'babel-loader?presets[]=react,presets[]=es2015,presets[]=stage-0'] 36 | }, 37 | // css 类型资源的处理 38 | { 39 | test: /\.css$/, 40 | include: /src\/styles\//, 41 | loader: 'style-loader!css-loader?importLoaders=2&sourceMap!postcss-loader?parser=postcss-scss' 42 | } 43 | ) 44 | }, 45 | plugins: [ 46 | // 引用打包好的第三方库 47 | new webpack.DllReferencePlugin({ 48 | context: __dirname, 49 | manifest: vendorsManifest 50 | }), 51 | // 把第三方库DLL文件拷贝到 prd 目录下 52 | new CopyWebpackPlugin([ 53 | { from: './dll/' + vendorsFileName, to: vendorsFileName } 54 | ]), 55 | // 把第三方库的依赖注入到代码中 56 | new webpack.ProvidePlugin(baseConfig.provideLibs), 57 | new HtmlWebpackPlugin({ 58 | filename: 'index.html', 59 | template: './html/index.html', 60 | vendors: vendorsFileName 61 | }), 62 | //增加HMR(模块热插拔) 63 | new webpack.HotModuleReplacementPlugin(), 64 | new webpack.NoErrorsPlugin(), 65 | // 构建时展示进度 66 | new ProgressPlugin(function(percentage, msg) { 67 | console.log(parseInt(percentage * 100) + '%', msg); 68 | }) 69 | ], 70 | postcss: baseConfig.postcss 71 | 72 | }; 73 | 74 | 75 | /** 76 | * 生成 dev 环境的主文件入口 77 | * @return {[type]} [description] 78 | */ 79 | function genEntry () { 80 | var webpackHMR = [ 81 | 'webpack-dev-server/client?http://127.0.0.1:3000', 82 | 'webpack/hot/only-dev-server' 83 | ]; 84 | var entry = {}; 85 | var baseEntry = baseConfig.entry; 86 | for(var key in baseEntry) { 87 | entry[key] = webpackHMR.concat(baseEntry[key]) 88 | } 89 | return entry; 90 | } -------------------------------------------------------------------------------- /src/scripts/components/common/resources/Resources.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | import ResourceList from './ResourceList'; 5 | let { Button, Icon } = Antd; 6 | let ButtonGroup = Button.Group; 7 | 8 | let columns = [ 9 | { 10 | title: '资源名称', 11 | dataIndex: 'resourceName', 12 | key: 'resourceName', 13 | width: '40%' 14 | }, 15 | { 16 | title: '资源标示', 17 | dataIndex: 'resourceId', 18 | key: 'resourceId', 19 | width: '40%' 20 | }, 21 | { 22 | title: '操作', 23 | dataIndex: '', 24 | key: 'operate', 25 | width: '20%', 26 | render: () => { 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | } 36 | ]; 37 | 38 | let rowSelection = { 39 | onChange(selectedRowKeys, selectedRows) { 40 | console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); 41 | }, 42 | onSelect(record, selected, selectedRows) { 43 | console.log(record, selected, selectedRows); 44 | }, 45 | onSelectAll(selected, selectedRows, changeRows) { 46 | console.log(selected, selectedRows, changeRows); 47 | } 48 | }; 49 | 50 | let menuData = [ 51 | { 52 | key: 'type-url', 53 | title: 'URL资源', 54 | link: '/page/projects/resource/url' 55 | }, 56 | { 57 | key: 'type-data', 58 | title: '录入资源', 59 | link: '/page/projects/resource/input' 60 | }, 61 | { 62 | key: 'type-api', 63 | title: '接口提供资源', 64 | link: '/page/projects/resource/api' 65 | } 66 | ]; 67 | 68 | 69 | export default class Resources extends React.Component { 70 | 71 | static defaultProps = { 72 | 73 | } 74 | 75 | state = { 76 | data: [] 77 | } 78 | 79 | constructor() { 80 | super(); 81 | } 82 | 83 | render() { 84 | return ( 85 |
    86 |
    87 |
    88 |
    89 | 90 |
    91 |
    92 | ); 93 | } 94 | 95 | componentDidMount() { 96 | this.setState({ 97 | data: [ 98 | { 99 | key: 1, 100 | resourceId: '/bnb/list', 101 | resourceName: '列表页', 102 | children: [ 103 | { 104 | key: 2, 105 | resourceId: '/bnb/subList', 106 | resourceName: '子列表' 107 | }, 108 | { 109 | key: 3, 110 | resourceId: '/bnb/attcheList', 111 | resourceName: '附列表' 112 | } 113 | ] 114 | }, 115 | { 116 | key: 4, 117 | resourceId: '/bnb/detail', 118 | resourceName: '详情页' 119 | }, 120 | { 121 | key: 5, 122 | resourceId: '/bnb/booking', 123 | resourceName: '报价页' 124 | } 125 | ] 126 | }); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /dll/vendors_manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vendors_b277224caea92e299ac1", 3 | "content": { 4 | "./node_modules/react/lib/ReactServerRenderingTransaction.js": 131, 5 | "./node_modules/isomorphic-fetch/node_modules/whatwg-fetch/fetch.js": 2, 6 | "./node_modules/react/react.js": 3, 7 | "./node_modules/react/lib/React.js": 4, 8 | "./node_modules/process/browser.js": 5, 9 | "./node_modules/react/node_modules/object-assign/index.js": 6, 10 | "./node_modules/react/lib/ReactChildren.js": 7, 11 | "./node_modules/react/lib/PooledClass.js": 8, 12 | "./node_modules/react/lib/reactProdInvariant.js": 9, 13 | "./node_modules/fbjs/lib/invariant.js": 10, 14 | "./node_modules/react/lib/ReactElement.js": 11, 15 | "./node_modules/react/lib/ReactCurrentOwner.js": 12, 16 | "./node_modules/fbjs/lib/warning.js": 13, 17 | "./node_modules/fbjs/lib/emptyFunction.js": 14, 18 | "./node_modules/react/lib/canDefineProperty.js": 15, 19 | "./node_modules/react/lib/traverseAllChildren.js": 16, 20 | "./node_modules/react/lib/getIteratorFn.js": 17, 21 | "./node_modules/react/lib/KeyEscapeUtils.js": 18, 22 | "./node_modules/react/lib/ReactComponent.js": 19, 23 | "./node_modules/react/lib/ReactNoopUpdateQueue.js": 20, 24 | "./node_modules/fbjs/lib/emptyObject.js": 21, 25 | "./node_modules/react/lib/ReactPureComponent.js": 22, 26 | "./node_modules/react/lib/ReactClass.js": 23, 27 | "./node_modules/react/lib/ReactPropTypeLocations.js": 24, 28 | "./node_modules/fbjs/lib/keyMirror.js": 25, 29 | "./node_modules/react/lib/ReactPropTypeLocationNames.js": 26, 30 | "./node_modules/fbjs/lib/keyOf.js": 27, 31 | "./node_modules/react/lib/ReactDOMFactories.js": 28, 32 | "./node_modules/react/lib/ReactElementValidator.js": 29, 33 | "./node_modules/react/lib/ReactComponentTreeHook.js": 30, 34 | "./node_modules/react/lib/checkReactTypeSpec.js": 31, 35 | "./node_modules/react/lib/ReactPropTypesSecret.js": 32, 36 | "./node_modules/react/lib/ReactPropTypes.js": 33, 37 | "./node_modules/react/lib/ReactVersion.js": 34, 38 | "./node_modules/react/lib/onlyChild.js": 35, 39 | "./node_modules/react-dom/index.js": 36, 40 | "./node_modules/react/lib/ReactDOM.js": 37, 41 | "./node_modules/react/lib/ReactDOMComponentTree.js": 38, 42 | "./node_modules/react/lib/DOMProperty.js": 39, 43 | "./node_modules/react/lib/ReactDOMComponentFlags.js": 40, 44 | "./node_modules/react/lib/ReactDefaultInjection.js": 41, 45 | "./node_modules/react/lib/BeforeInputEventPlugin.js": 42, 46 | "./node_modules/react/lib/EventConstants.js": 43, 47 | "./node_modules/react/lib/EventPropagators.js": 44, 48 | "./node_modules/react/lib/EventPluginHub.js": 45, 49 | "./node_modules/react/lib/EventPluginRegistry.js": 46, 50 | "./node_modules/react/lib/EventPluginUtils.js": 47, 51 | "./node_modules/react/lib/ReactErrorUtils.js": 48, 52 | "./node_modules/react/lib/accumulateInto.js": 49, 53 | "./node_modules/react/lib/forEachAccumulated.js": 50, 54 | "./node_modules/fbjs/lib/ExecutionEnvironment.js": 51, 55 | "./node_modules/react/lib/FallbackCompositionState.js": 52, 56 | "./node_modules/react/lib/getTextContentAccessor.js": 53, 57 | "./node_modules/react/lib/SyntheticCompositionEvent.js": 54, 58 | "./node_modules/react/lib/SyntheticEvent.js": 55, 59 | "./node_modules/react/lib/SyntheticInputEvent.js": 56, 60 | "./node_modules/react/lib/ChangeEventPlugin.js": 57, 61 | "./node_modules/react/lib/ReactUpdates.js": 58, 62 | "./node_modules/react/lib/CallbackQueue.js": 59, 63 | "./node_modules/react/lib/ReactFeatureFlags.js": 60, 64 | "./node_modules/react/lib/ReactReconciler.js": 61, 65 | "./node_modules/react/lib/ReactRef.js": 62, 66 | "./node_modules/react/lib/ReactOwner.js": 63, 67 | "./node_modules/react/lib/ReactInstrumentation.js": 64, 68 | "./node_modules/react/lib/ReactDebugTool.js": 65, 69 | "./node_modules/react/lib/ReactInvalidSetStateWarningHook.js": 66, 70 | "./node_modules/react/lib/ReactHostOperationHistoryHook.js": 67, 71 | "./node_modules/react/lib/ReactChildrenMutationWarningHook.js": 68, 72 | "./node_modules/fbjs/lib/performanceNow.js": 69, 73 | "./node_modules/fbjs/lib/performance.js": 70, 74 | "./node_modules/react/lib/Transaction.js": 71, 75 | "./node_modules/react/lib/getEventTarget.js": 72, 76 | "./node_modules/react/lib/isEventSupported.js": 73, 77 | "./node_modules/react/lib/isTextInputElement.js": 74, 78 | "./node_modules/react/lib/DefaultEventPluginOrder.js": 75, 79 | "./node_modules/react/lib/EnterLeaveEventPlugin.js": 76, 80 | "./node_modules/react/lib/SyntheticMouseEvent.js": 77, 81 | "./node_modules/react/lib/SyntheticUIEvent.js": 78, 82 | "./node_modules/react/lib/ViewportMetrics.js": 79, 83 | "./node_modules/react/lib/getEventModifierState.js": 80, 84 | "./node_modules/react/lib/HTMLDOMPropertyConfig.js": 81, 85 | "./node_modules/react/lib/ReactComponentBrowserEnvironment.js": 82, 86 | "./node_modules/react/lib/DOMChildrenOperations.js": 83, 87 | "./node_modules/react/lib/DOMLazyTree.js": 84, 88 | "./node_modules/react/lib/DOMNamespaces.js": 85, 89 | "./node_modules/react/lib/setInnerHTML.js": 86, 90 | "./node_modules/react/lib/createMicrosoftUnsafeLocalFunction.js": 87, 91 | "./node_modules/react/lib/setTextContent.js": 88, 92 | "./node_modules/react/lib/escapeTextContentForBrowser.js": 89, 93 | "./node_modules/react/lib/Danger.js": 90, 94 | "./node_modules/fbjs/lib/createNodesFromMarkup.js": 91, 95 | "./node_modules/fbjs/lib/createArrayFromMixed.js": 92, 96 | "./node_modules/fbjs/lib/getMarkupWrap.js": 93, 97 | "./node_modules/react/lib/ReactMultiChildUpdateTypes.js": 94, 98 | "./node_modules/react/lib/ReactDOMIDOperations.js": 95, 99 | "./node_modules/react/lib/ReactDOMComponent.js": 96, 100 | "./node_modules/react/lib/AutoFocusUtils.js": 97, 101 | "./node_modules/fbjs/lib/focusNode.js": 98, 102 | "./node_modules/react/lib/CSSPropertyOperations.js": 99, 103 | "./node_modules/react/lib/CSSProperty.js": 100, 104 | "./node_modules/fbjs/lib/camelizeStyleName.js": 101, 105 | "./node_modules/fbjs/lib/camelize.js": 102, 106 | "./node_modules/react/lib/dangerousStyleValue.js": 103, 107 | "./node_modules/fbjs/lib/hyphenateStyleName.js": 104, 108 | "./node_modules/fbjs/lib/hyphenate.js": 105, 109 | "./node_modules/fbjs/lib/memoizeStringOnly.js": 106, 110 | "./node_modules/react/lib/DOMPropertyOperations.js": 107, 111 | "./node_modules/react/lib/quoteAttributeValueForBrowser.js": 108, 112 | "./node_modules/react/lib/ReactBrowserEventEmitter.js": 109, 113 | "./node_modules/react/lib/ReactEventEmitterMixin.js": 110, 114 | "./node_modules/react/lib/getVendorPrefixedEventName.js": 111, 115 | "./node_modules/react/lib/ReactDOMButton.js": 112, 116 | "./node_modules/react/lib/DisabledInputUtils.js": 113, 117 | "./node_modules/react/lib/ReactDOMInput.js": 114, 118 | "./node_modules/react/lib/LinkedValueUtils.js": 115, 119 | "./node_modules/react/lib/ReactDOMOption.js": 116, 120 | "./node_modules/react/lib/ReactDOMSelect.js": 117, 121 | "./node_modules/react/lib/ReactDOMTextarea.js": 118, 122 | "./node_modules/react/lib/ReactMultiChild.js": 119, 123 | "./node_modules/react/lib/ReactComponentEnvironment.js": 120, 124 | "./node_modules/react/lib/ReactInstanceMap.js": 121, 125 | "./node_modules/react/lib/ReactChildReconciler.js": 122, 126 | "./node_modules/react/lib/instantiateReactComponent.js": 123, 127 | "./node_modules/react/lib/ReactCompositeComponent.js": 124, 128 | "./node_modules/react/lib/ReactNodeTypes.js": 125, 129 | "./node_modules/fbjs/lib/shallowEqual.js": 126, 130 | "./node_modules/react/lib/shouldUpdateReactComponent.js": 127, 131 | "./node_modules/react/lib/ReactEmptyComponent.js": 128, 132 | "./node_modules/react/lib/ReactHostComponent.js": 129, 133 | "./node_modules/react/lib/flattenChildren.js": 130, 134 | "./node_modules/isomorphic-fetch/fetch-npm-browserify.js": 1, 135 | "./node_modules/react/lib/ReactServerUpdateQueue.js": 132, 136 | "./node_modules/react/lib/ReactUpdateQueue.js": 133, 137 | "./node_modules/react/lib/validateDOMNesting.js": 134, 138 | "./node_modules/react/lib/ReactDOMEmptyComponent.js": 135, 139 | "./node_modules/react/lib/ReactDOMTreeTraversal.js": 136, 140 | "./node_modules/react/lib/ReactDOMTextComponent.js": 137, 141 | "./node_modules/react/lib/ReactDefaultBatchingStrategy.js": 138, 142 | "./node_modules/react/lib/ReactEventListener.js": 139, 143 | "./node_modules/fbjs/lib/EventListener.js": 140, 144 | "./node_modules/fbjs/lib/getUnboundedScrollPosition.js": 141, 145 | "./node_modules/react/lib/ReactInjection.js": 142, 146 | "./node_modules/react/lib/ReactReconcileTransaction.js": 143, 147 | "./node_modules/react/lib/ReactInputSelection.js": 144, 148 | "./node_modules/react/lib/ReactDOMSelection.js": 145, 149 | "./node_modules/react/lib/getNodeForCharacterOffset.js": 146, 150 | "./node_modules/fbjs/lib/containsNode.js": 147, 151 | "./node_modules/fbjs/lib/isTextNode.js": 148, 152 | "./node_modules/fbjs/lib/isNode.js": 149, 153 | "./node_modules/fbjs/lib/getActiveElement.js": 150, 154 | "./node_modules/react/lib/SVGDOMPropertyConfig.js": 151, 155 | "./node_modules/react/lib/SelectEventPlugin.js": 152, 156 | "./node_modules/react/lib/SimpleEventPlugin.js": 153, 157 | "./node_modules/react/lib/SyntheticAnimationEvent.js": 154, 158 | "./node_modules/react/lib/SyntheticClipboardEvent.js": 155, 159 | "./node_modules/react/lib/SyntheticFocusEvent.js": 156, 160 | "./node_modules/react/lib/SyntheticKeyboardEvent.js": 157, 161 | "./node_modules/react/lib/getEventCharCode.js": 158, 162 | "./node_modules/react/lib/getEventKey.js": 159, 163 | "./node_modules/react/lib/SyntheticDragEvent.js": 160, 164 | "./node_modules/react/lib/SyntheticTouchEvent.js": 161, 165 | "./node_modules/react/lib/SyntheticTransitionEvent.js": 162, 166 | "./node_modules/react/lib/SyntheticWheelEvent.js": 163, 167 | "./node_modules/react/lib/ReactMount.js": 164, 168 | "./node_modules/react/lib/ReactDOMContainerInfo.js": 165, 169 | "./node_modules/react/lib/ReactDOMFeatureFlags.js": 166, 170 | "./node_modules/react/lib/ReactMarkupChecksum.js": 167, 171 | "./node_modules/react/lib/adler32.js": 168, 172 | "./node_modules/react/lib/findDOMNode.js": 169, 173 | "./node_modules/react/lib/getHostComponentFromComposite.js": 170, 174 | "./node_modules/react/lib/renderSubtreeIntoContainer.js": 171, 175 | "./node_modules/react/lib/ReactDOMUnknownPropertyHook.js": 172, 176 | "./node_modules/react/lib/ReactDOMNullInputValuePropHook.js": 173, 177 | "./node_modules/react-router/lib/index.js": 174, 178 | "./node_modules/react-router/lib/RouteUtils.js": 175, 179 | "./node_modules/react-router/lib/PropTypes.js": 176, 180 | "./node_modules/react-router/lib/deprecateObjectProperties.js": 177, 181 | "./node_modules/react-router/lib/routerWarning.js": 178, 182 | "./node_modules/react-router/node_modules/warning/browser.js": 179, 183 | "./node_modules/react-router/lib/InternalPropTypes.js": 180, 184 | "./node_modules/react-router/lib/PatternUtils.js": 181, 185 | "./node_modules/invariant/browser.js": 182, 186 | "./node_modules/react-router/lib/Router.js": 183, 187 | "./node_modules/history/lib/createHashHistory.js": 184, 188 | "./node_modules/warning/browser.js": 185, 189 | "./node_modules/history/lib/Actions.js": 186, 190 | "./node_modules/history/lib/PathUtils.js": 187, 191 | "./node_modules/history/lib/ExecutionEnvironment.js": 188, 192 | "./node_modules/history/lib/DOMUtils.js": 189, 193 | "./node_modules/history/lib/DOMStateStorage.js": 190, 194 | "./node_modules/history/lib/createDOMHistory.js": 191, 195 | "./node_modules/history/lib/createHistory.js": 192, 196 | "./node_modules/deep-equal/index.js": 193, 197 | "./node_modules/deep-equal/lib/keys.js": 194, 198 | "./node_modules/deep-equal/lib/is_arguments.js": 195, 199 | "./node_modules/history/lib/AsyncUtils.js": 196, 200 | "./node_modules/history/lib/createLocation.js": 197, 201 | "./node_modules/history/lib/runTransitionHook.js": 198, 202 | "./node_modules/history/lib/deprecate.js": 199, 203 | "./node_modules/history/lib/useQueries.js": 200, 204 | "./node_modules/query-string/index.js": 201, 205 | "./node_modules/strict-uri-encode/index.js": 202, 206 | "./node_modules/react-router/lib/createTransitionManager.js": 203, 207 | "./node_modules/react-router/lib/computeChangedRoutes.js": 204, 208 | "./node_modules/react-router/lib/TransitionUtils.js": 205, 209 | "./node_modules/react-router/lib/AsyncUtils.js": 206, 210 | "./node_modules/react-router/lib/isActive.js": 207, 211 | "./node_modules/react-router/lib/getComponents.js": 208, 212 | "./node_modules/react-router/lib/makeStateWithLocation.js": 209, 213 | "./node_modules/react-router/lib/matchRoutes.js": 210, 214 | "./node_modules/react-router/lib/RouterContext.js": 211, 215 | "./node_modules/react-router/lib/getRouteParams.js": 212, 216 | "./node_modules/react-router/lib/RouterUtils.js": 213, 217 | "./node_modules/react-router/lib/Link.js": 214, 218 | "./node_modules/react-router/lib/IndexLink.js": 215, 219 | "./node_modules/react-router/lib/withRouter.js": 216, 220 | "./node_modules/hoist-non-react-statics/index.js": 217, 221 | "./node_modules/react-router/lib/IndexRedirect.js": 218, 222 | "./node_modules/react-router/lib/Redirect.js": 219, 223 | "./node_modules/react-router/lib/IndexRoute.js": 220, 224 | "./node_modules/react-router/lib/Route.js": 221, 225 | "./node_modules/react-router/lib/History.js": 222, 226 | "./node_modules/react-router/lib/Lifecycle.js": 223, 227 | "./node_modules/react-router/lib/RouteContext.js": 224, 228 | "./node_modules/react-router/lib/useRoutes.js": 225, 229 | "./node_modules/react-router/lib/RoutingContext.js": 226, 230 | "./node_modules/react-router/lib/match.js": 227, 231 | "./node_modules/react-router/lib/createMemoryHistory.js": 228, 232 | "./node_modules/history/lib/useBasename.js": 229, 233 | "./node_modules/history/lib/createMemoryHistory.js": 230, 234 | "./node_modules/react-router/lib/useRouterHistory.js": 231, 235 | "./node_modules/react-router/lib/applyRouterMiddleware.js": 232, 236 | "./node_modules/react-router/lib/browserHistory.js": 233, 237 | "./node_modules/history/lib/createBrowserHistory.js": 234, 238 | "./node_modules/react-router/lib/createRouterHistory.js": 235, 239 | "./node_modules/react-router/lib/hashHistory.js": 236, 240 | "./node_modules/redux/lib/index.js": 237, 241 | "./node_modules/redux/lib/createStore.js": 238, 242 | "./node_modules/lodash/isPlainObject.js": 239, 243 | "./node_modules/lodash/_getPrototype.js": 240, 244 | "./node_modules/lodash/_overArg.js": 241, 245 | "./node_modules/lodash/_isHostObject.js": 242, 246 | "./node_modules/lodash/isObjectLike.js": 243, 247 | "./node_modules/symbol-observable/index.js": 244, 248 | "./node_modules/symbol-observable/ponyfill.js": 245, 249 | "./node_modules/redux/lib/combineReducers.js": 246, 250 | "./node_modules/redux/lib/utils/warning.js": 247, 251 | "./node_modules/redux/lib/bindActionCreators.js": 248, 252 | "./node_modules/redux/lib/applyMiddleware.js": 249, 253 | "./node_modules/redux/lib/compose.js": 250, 254 | "./node_modules/redux-thunk/lib/index.js": 251, 255 | "./node_modules/react-redux/lib/index.js": 252, 256 | "./node_modules/react-redux/lib/components/Provider.js": 253, 257 | "./node_modules/react-redux/lib/utils/storeShape.js": 254, 258 | "./node_modules/react-redux/lib/utils/warning.js": 255, 259 | "./node_modules/react-redux/lib/components/connect.js": 256, 260 | "./node_modules/react-redux/lib/utils/shallowEqual.js": 257, 261 | "./node_modules/react-redux/lib/utils/wrapActionCreators.js": 258, 262 | "./node_modules/redux-promise-middleware/dist/index.js": 259, 263 | "./node_modules/redux-promise-middleware/dist/isPromise.js": 260, 264 | "./node_modules/antd/dist/antd.js": 261 265 | } 266 | } --------------------------------------------------------------------------------