├── dev ├── chunk-manifest.json ├── index.html └── js │ ├── manifest.js.map │ └── manifest.js ├── app ├── server │ ├── model │ │ ├── index.js │ │ ├── users.js │ │ └── articles.js │ ├── config │ │ └── index.js │ ├── schemas │ │ ├── users.js │ │ └── articles.js │ ├── api │ │ ├── index.js │ │ ├── logout.js │ │ ├── register.js │ │ ├── admin.js │ │ └── login.js │ ├── middlewares │ │ └── auth.js │ └── server.js └── front │ ├── js │ ├── actions │ │ ├── about.js │ │ ├── index.js │ │ ├── users.js │ │ └── articles.js │ ├── components │ │ ├── Admin │ │ │ ├── index.js │ │ │ ├── Admin.js │ │ │ ├── AdminLink.js │ │ │ ├── Users.js │ │ │ ├── EditArticle.js │ │ │ ├── Article.js │ │ │ └── AdminHome.js │ │ ├── Blog │ │ │ ├── All.js │ │ │ ├── Auto.js │ │ │ ├── Mongo.js │ │ │ ├── Node.js │ │ │ ├── Blog.js │ │ │ ├── index.js │ │ │ └── BlogLink.js │ │ ├── partial │ │ │ └── index.js │ │ ├── Header │ │ │ └── index.js │ │ ├── About │ │ │ └── index.js │ │ ├── NotFound │ │ │ └── index.js │ │ ├── GetStorage │ │ │ └── index.js │ │ └── NavLink │ │ │ └── index.js │ ├── store │ │ ├── index.js │ │ ├── store.prod.js │ │ └── store.dev.js │ ├── reducers │ │ ├── home.js │ │ ├── about.js │ │ ├── users.js │ │ ├── index.js │ │ ├── userInfo.js │ │ └── articles.js │ ├── constants │ │ └── index.js │ ├── utils │ │ ├── enterOrLeaveRoute.js │ │ └── request.js │ ├── containers │ │ ├── Home │ │ │ └── index.js │ │ ├── App.js │ │ ├── Login │ │ │ └── index.js │ │ └── Register │ │ │ └── index.js │ ├── index.js │ └── router │ │ └── index.js │ └── scss │ ├── app.scss │ ├── admin.scss │ ├── pubilc.scss │ ├── login.scss │ ├── nav.scss │ ├── header.scss │ └── reset.scss ├── .idea ├── dictionaries │ └── shunshun.xml ├── vcs.xml ├── jsLibraryMappings.xml ├── modules.xml ├── react-express-mongodb.iml ├── misc.xml └── inspectionProfiles │ └── Project_Default.xml ├── .eslintignore ├── .gitignore ├── tsconfig.json ├── webpack ├── api.js ├── config.js └── base.js ├── .babelrc ├── README.md ├── webpack.prod.config.js ├── static └── tpl.html ├── dev-server.js ├── package.json ├── webpack.dev.config.js └── .eslintrc /dev/chunk-manifest.json: -------------------------------------------------------------------------------- 1 | {"0":"app.chunk.js","1":"verdor.chunk.js"} -------------------------------------------------------------------------------- /app/server/model/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/14. 3 | */ 4 | -------------------------------------------------------------------------------- /app/server/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/14. 3 | */ 4 | -------------------------------------------------------------------------------- /.idea/dictionaries/shunshun.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.dev.config.js 2 | webpack.prod.config.js 3 | webpack/**/* 4 | node-modules/**/* 5 | dev/**/* 6 | build/**/* 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tags 2 | *.tags1 3 | */.DS_Store 4 | *.DS_Store 5 | */.idea/ 6 | .idea/ 7 | /node_modules/ 8 | /app/js/ 9 | /app/scss/ 10 | /app/server/db/ 11 | /dev/ -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/front/js/actions/about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import * as types from '../constants'; 5 | 6 | export const receiveUserInfo = (data) => ({ 7 | type: types.FETCH_USER_INFO, 8 | info: data 9 | }); 10 | -------------------------------------------------------------------------------- /app/server/model/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/16. 3 | */ 4 | const mongoose = require('mongoose'); 5 | const userSchema = require('../schemas/users'); 6 | // console.log(userSchema); 7 | module.exports = mongoose.model('Users', userSchema); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 10:14:53 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-14 10:15:40 6 | */ 7 | { 8 | "compilerOptions": { 9 | "experimentalDecorators": true, 10 | "allowJs": true 11 | } 12 | } -------------------------------------------------------------------------------- /app/front/js/components/Admin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/19. 3 | */ 4 | 5 | export { Users } from './Users' 6 | export { Admin } from './Admin' 7 | export { AdminHome } from './AdminHome' 8 | export { Article } from './Article' 9 | export { EditArticle } from './EditArticle' -------------------------------------------------------------------------------- /app/front/js/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | if (process.env.NODE_ENV === 'production') { 5 | console.log(222222222222); 6 | module.exports = require('./store.prod'); 7 | } else { 8 | console.log('111111'); 9 | module.exports = require('./store.dev'); 10 | } -------------------------------------------------------------------------------- /app/server/schemas/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/16. 3 | */ 4 | const mongoose = require('mongoose'); 5 | const Schema = mongoose.Schema; 6 | module.exports = new Schema({ 7 | username: String, 8 | password: String, 9 | isAdmin: { 10 | type: Boolean, 11 | default: false 12 | } 13 | }) -------------------------------------------------------------------------------- /app/front/scss/app.scss: -------------------------------------------------------------------------------- 1 | .wraper { 2 | width: 100%; 3 | // height: 100%; 4 | // overflow: hidden; 5 | .main { 6 | width: 90%; 7 | // height: 90%; 8 | padding-top: 20px; 9 | // overflow: hidden; 10 | margin: 0 auto; 11 | .right-content { 12 | width: 87%; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/server/model/articles.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 16:02:13 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-14 18:42:11 6 | */ 7 | const mongoose = require('mongoose'); 8 | const articleSchema = require('../schemas/articles'); 9 | module.exports = mongoose.model('Articles', articleSchema); -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/front/js/components/Blog/All.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | export class All extends React.Component { 6 | render() { 7 | return ( 8 |
9 |
All
10 |
11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /app/front/js/components/Blog/Auto.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | export class Auto extends React.Component { 6 | render() { 7 | return ( 8 |
9 |
Auto
10 |
11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /app/front/js/components/Blog/Mongo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | export class Mongo extends React.Component { 6 | render() { 7 | return ( 8 |
9 |
Mongo
10 |
11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /app/front/js/components/Blog/Node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | export class Node extends React.Component { 6 | render() { 7 | return ( 8 |
9 |
NodeJS
10 |
11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /webpack/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by "苏萧" on 2017/7/11. 3 | */ 4 | /** 5 | * 定义不同环境变量下的 应用请求配置---服务器 API 配置文件 6 | * */ 7 | 8 | const ip = require("ip").address(); 9 | 10 | module.exports = { 11 | test: "http://www.baidu.com",// 测试环境的服务器地址 12 | development: `http://${ip}:3333`,// 开发环境的本地服务器地址 13 | production: "http://www.google.com"// 生产环境的服务器地址 14 | }; -------------------------------------------------------------------------------- /app/front/scss/admin.scss: -------------------------------------------------------------------------------- 1 | .admin { 2 | 3 | } 4 | .admin-nav { 5 | ul { 6 | display: flex; 7 | justify-content: flex-start; 8 | li { 9 | margin: 0 5px; 10 | } 11 | a { 12 | padding: 5px; 13 | } 14 | } 15 | } 16 | .active { 17 | color: #ff6600; 18 | } 19 | table { 20 | border: none !important; 21 | } 22 | th, td { 23 | text-align: center !important; 24 | border-right: none !important; 25 | } -------------------------------------------------------------------------------- /app/front/js/components/Blog/Blog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/14. 3 | */ 4 | import React from 'react'; 5 | import BlogLink from './BlogLink'; 6 | 7 | export class Blog extends React.Component { 8 | render() { 9 | return ( 10 |
11 | 12 |
13 | {this.props.children} 14 |
15 |
16 | ); 17 | } 18 | } -------------------------------------------------------------------------------- /app/server/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/14. 3 | */ 4 | const express = require('express'); 5 | const router = express.Router(); 6 | // import { Router } from 'express'; 7 | 8 | // const router = new Router(); 9 | 10 | router.use('/login', require('./login')); 11 | router.use('/register', require('./register')); 12 | router.use('/admin', require('./admin')); 13 | router.use('/logout', require('./logout')); 14 | 15 | module.exports = router; -------------------------------------------------------------------------------- /app/server/api/logout.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-17 11:47:40 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-17 15:56:35 6 | */ 7 | const express = require('express'); 8 | const router = express.Router(); 9 | router.post('/', (req, res, next) => { 10 | req.session.destroy(); 11 | res.clearCookie('user-token', { 12 | path: '/', 13 | domain: '' 14 | }); 15 | res.redirect('/sign'); 16 | }) 17 | 18 | module.exports = router; -------------------------------------------------------------------------------- /app/front/js/components/partial/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/12. 3 | */ 4 | import React from 'react' 5 | import { Link } from 'react-router' 6 | import { Icon } from 'antd' 7 | 8 | export default class Partial extends React.Component { 9 | render() { 10 | return ( 11 |
12 | { 13 | this.props.type && 14 | } 15 | 16 |
17 | ); 18 | } 19 | } -------------------------------------------------------------------------------- /.idea/react-express-mongodb.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/front/js/store/store.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import { compose, applyMiddleware } from 'redux'; 5 | import thunkMiddleware from 'redux-thunk'; 6 | 7 | import rootReducer from '../reducers' 8 | 9 | const createStoreWithMiddleware = compose( 10 | applyMiddleware( 11 | thunkMiddleware 12 | ) 13 | ); 14 | 15 | const configureStore = (initialState) => { 16 | const store = createStoreWithMiddleware(rootReducer, initialState); 17 | return store; 18 | } 19 | 20 | export default configureStore -------------------------------------------------------------------------------- /app/front/js/components/Blog/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | // import React from 'react' 5 | // import NavLink from '../NavLink' 6 | // export default class Blog extends React.Component { 7 | // render() { 8 | // return ( 9 | //
10 | // 11 | //
12 | // ); 13 | // } 14 | // } 15 | 16 | export { All } from './All'; 17 | export { Auto } from './Auto'; 18 | export { Mongo } from './Mongo'; 19 | export { Node } from './Node'; 20 | export { Blog } from './Blog'; -------------------------------------------------------------------------------- /app/front/js/components/Admin/Admin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/19. 3 | */ 4 | import React, { Component } from 'react'; 5 | 6 | import AdminLink from './AdminLink' 7 | import '../../../scss/admin.scss' 8 | 9 | export class Admin extends Component { 10 | constructor (props) { 11 | super(props) 12 | } 13 | 14 | render() { 15 | return ( 16 |
17 | 18 |
19 | {this.props.children} 20 |
21 |
22 | ) 23 | } 24 | } -------------------------------------------------------------------------------- /app/server/schemas/articles.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 16:03:01 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-14 21:42:11 6 | */ 7 | const mongoose = require('mongoose'); 8 | const Schema = mongoose.Schema; 9 | module.exports = new Schema({ 10 | sort: String, 11 | title: String, 12 | content: String, 13 | author: { 14 | type: String, 15 | default: '' 16 | }, 17 | createDate: { 18 | type: Date, 19 | default: Date.now 20 | }, 21 | updateDate: { 22 | type: Date, 23 | default: Date.now 24 | } 25 | }) -------------------------------------------------------------------------------- /app/front/js/reducers/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/15. 3 | */ 4 | import * as types from '../constants'; 5 | const initialState = { 6 | title: 'Home', 7 | description: '这是一个个人博客' 8 | } 9 | 10 | const homeState = (state=initialState, action) => { 11 | switch (action.type) { 12 | case types.HOME_INFO: 13 | let info = {}; 14 | if (action.info !== undefined) { 15 | info = action.info 16 | } 17 | return Object.assign({}, state, { ...info }); 18 | default: 19 | return state 20 | } 21 | } 22 | 23 | export default homeState -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "presets": [ 4 | ["es2015", { "modules": false }], 5 | "stage-2", 6 | "react" 7 | ], 8 | "plugins": [ 9 | "react-hot-loader/babel", 10 | "transform-object-assign", 11 | "transform-decorators-legacy", 12 | [ 13 | "transform-runtime", 14 | [ 15 | "import", 16 | { "libraryName": "antd", "style": true } 17 | ], 18 | { "polyfill": false, "regenerator": true } 19 | ] 20 | ], 21 | "env": { 22 | "development": { 23 | "plugins": [ 24 | "react-hot-loader/babel" 25 | ] 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/front/js/reducers/about.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import * as types from '../constants'; 5 | 6 | const initialState = { 7 | info: 'about', 8 | isFetching: true 9 | }; 10 | 11 | const aboutState = (state=initialState, action) => { 12 | switch (action) { 13 | case types.FETCH_USER_INFO: 14 | let info = {}; 15 | if (action.info !== undefined) { 16 | info = action.info 17 | } 18 | return Object.assign({}, state, {info:info, isFetching:false}); 19 | default: 20 | return state 21 | } 22 | }; 23 | 24 | export default aboutState -------------------------------------------------------------------------------- /app/front/js/components/Header/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | import { Row, Col } from 'antd'; 6 | import NavLink from '../NavLink' 7 | import '../../../scss/header.scss' 8 | 9 | export default class Header extends React.Component { 10 | render() { 11 | return ( 12 | 13 | 14 |

小黑哥的BLOG

15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /app/front/js/components/About/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | import { connect } from 'react-redux' 6 | 7 | import { receiveUserInfo } from '../../actions/about' 8 | @connect( 9 | (state) => ({about: state.about}), 10 | (dispath) => ({receiveUserInfo }) 11 | ) 12 | 13 | export default class About extends React.Component { 14 | render() { 15 | console.log(this.props.about.info); 16 | // let items = [1, 2, 3].map( (value, index) => { 17 | // return
{value}
; 18 | // }); 19 | // console.log(items); 20 | return ( 21 |
{this.props.about.info}
22 | ); 23 | } 24 | } -------------------------------------------------------------------------------- /app/front/js/components/NotFound/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | import {browserHistory} from 'react-router'; 6 | 7 | export default class NotFound extends React.Component { 8 | handleClick = () => { 9 | browserHistory.push('/home'); 10 | } 11 | render() { 12 | return ( 13 |
14 |
15 |

页面没有找到...

16 |
17 |
18 | 21 |
22 |
23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /app/front/js/reducers/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/20. 3 | */ 4 | import * as types from '../constants'; 5 | const initialState = { 6 | users: [], 7 | isFetching: true 8 | } 9 | 10 | const usersState = (state=initialState, action) => { 11 | switch (action.type) { 12 | case types.FETCH_REQUEST: 13 | return Object({}, state, {isFetching: true}) 14 | case types.FETCH_USERS: 15 | let info = {}; 16 | if (action.users !== undefined) { 17 | info = action.users 18 | } 19 | return Object.assign({}, state, { users: info, isFetching: false }); 20 | default: 21 | return state 22 | } 23 | } 24 | 25 | export default usersState -------------------------------------------------------------------------------- /app/front/scss/pubilc.scss: -------------------------------------------------------------------------------- 1 | .clearfix { 2 | zoom: 1; 3 | } 4 | 5 | .clearfix:after { 6 | content: "."; 7 | width: 0; 8 | height: 0; 9 | visibility: hidden; 10 | display: block; 11 | clear: both; 12 | overflow:hidden; 13 | } 14 | 15 | .fl { 16 | float: left 17 | } 18 | 19 | .fr { 20 | float: right 21 | } 22 | 23 | .tl { 24 | text-align: left; 25 | } 26 | 27 | .tc { 28 | text-align: center 29 | } 30 | 31 | .tr { 32 | text-align: right; 33 | } 34 | 35 | .ellipse { 36 | overflow: hidden; 37 | text-overflow: ellipsis; 38 | white-space: nowrap; 39 | } 40 | .inline{ 41 | display: inline-block; 42 | *display: inline; 43 | *zoom: 1; 44 | } 45 | .h100 { 46 | height: 100%; 47 | } -------------------------------------------------------------------------------- /app/front/scss/login.scss: -------------------------------------------------------------------------------- 1 | .login { 2 | width: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | justify-content: center; 7 | font-size: 16px; 8 | line-height: 26px; 9 | form { 10 | padding: 20px; 11 | margin: 0 auto; 12 | .input-wrapper { 13 | margin-bottom: 20px; 14 | text-align: center; 15 | } 16 | input { 17 | border: 1px solid #999; 18 | height: 26px; 19 | padding-left: 10px; 20 | } 21 | input[type='submit'] { 22 | padding: 5px 10px; 23 | } 24 | } 25 | } 26 | .login-form { 27 | max-width: 300px; 28 | } 29 | .login-form-forgot { 30 | float: right; 31 | } 32 | .login-form-button { 33 | width: 100%; 34 | } -------------------------------------------------------------------------------- /app/front/js/reducers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import { combineReducers } from 'redux'; 5 | import {routerReducer} from 'react-router-redux'; 6 | import {reducer as reduceForm} from 'redux-form'; 7 | 8 | import aboutState from './about'; 9 | import homeState from './home'; 10 | import usersState from './users'; 11 | import userInfo from './userInfo'; 12 | import articlesState from './articles'; 13 | 14 | const rootReducer = combineReducers({ 15 | about: aboutState, 16 | home: homeState, 17 | users: usersState, 18 | userInfo: userInfo, 19 | articles: articlesState, 20 | form: reduceForm, 21 | // 挂载redux-form 如果不挂载会报一堆的错误 22 | routing: routerReducer 23 | }); 24 | 25 | export default rootReducer; -------------------------------------------------------------------------------- /app/front/js/components/GetStorage/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-10-19 17:10:11 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-20 15:59:29 6 | */ 7 | 8 | // 自动生成注释 control + option + i 9 | import React, { Component } from 'react'; 10 | 11 | const GetStorage = (key) => (WrappedComponent) => class extends Component { 12 | componentWillMount() { 13 | const data = localStorage.getItem(key); 14 | this.setState({data}); 15 | } 16 | 17 | testClick() { 18 | console.log('----test----'); 19 | } 20 | 21 | render() { 22 | return 23 | } 24 | } 25 | 26 | export default GetStorage; 27 | -------------------------------------------------------------------------------- /app/front/scss/nav.scss: -------------------------------------------------------------------------------- 1 | .left-menu { 2 | background: rgb(64, 64 ,64); 3 | width: 13%; 4 | height: 100%; 5 | h3 { 6 | height: 44px; 7 | line-height: 44px; 8 | background: #000; 9 | color: #ea8010; 10 | padding-left: 14px; 11 | font-size:18px; 12 | border-bottom: 1px solid #ea8010; 13 | } 14 | .nav { 15 | font-size: 16px; 16 | font-weigth: 400; 17 | li { 18 | height: 44px; 19 | line-height: 44px; 20 | a { 21 | display: block; 22 | padding-left: 14px; 23 | width: 100%; 24 | height: 100%; 25 | color: #fff; 26 | } 27 | .active { 28 | background: rgb(36, 35, 40); 29 | color: mediumvioletred; 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/front/js/constants/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | 5 | // import { createAction } from 'redux-actions'; 本来想用的,但是感觉更麻烦; 6 | // 同意的发起请求和请求失败 7 | export const FETCH_REQUEST = 'FETCH_REQUEST'; 8 | export const FETCH_FAILURE = 'FETCH_FAILURE'; 9 | // 登录后用户信息 10 | export const FETCH_USER_INFO = 'FETCH_USER_INFO'; 11 | // 请求文章列表 12 | export const FETCH_ARTICLES = 'FETCH_ARTICLES'; 13 | // 新增文章 14 | export const FETCH_ADD_ARTICLE = 'FETCH_ADD_ARTICLE'; 15 | // 请求用户 16 | export const FETCH_USERS = 'FETCH_USERS'; 17 | export const HOME_INFO = 'HOME_INFO'; 18 | 19 | // 请求文章 20 | // export const fetchArticles = createAction(FETCH_ARTICLES, articles => articles); 21 | // 请求用户 22 | // export const fetchUsers = createAction(FETCH_USERS, users => users); -------------------------------------------------------------------------------- /app/front/js/components/Blog/BlogLink.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react'; 5 | import Partial from '../partial'; 6 | import '../../../scss/nav.scss'; 7 | 8 | export default class BlogLink extends React.Component { 9 | render() { 10 | return ( 11 |
12 |

文章分类

13 | 21 |
22 | ); 23 | } 24 | } -------------------------------------------------------------------------------- /app/front/js/reducers/userInfo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-13 14:11:33 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-15 11:22:14 6 | */ 7 | import * as types from '../constants'; 8 | const initialState = { 9 | user: {}, 10 | isFetching: true 11 | } 12 | 13 | const userInfo = (state=initialState, action) => { 14 | switch (action.type) { 15 | case types.FETCH_REQUEST: 16 | return Object({}, state, {isFetching: true}) 17 | case types.FETCH_USER_INFO: 18 | let info = {}; 19 | if (action.user !== undefined) { 20 | info = action.user; 21 | } 22 | return Object.assign({}, state, { ...info, isFetching: false }); 23 | default: 24 | return state 25 | } 26 | } 27 | 28 | export default userInfo -------------------------------------------------------------------------------- /app/front/js/actions/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import * as types from '../constants'; 5 | import { get } from '../utils/request'; 6 | 7 | const fetchRequset = () => ({ 8 | type: types.FETCH_REQUEST 9 | }) 10 | 11 | const fetchSuccess = (users) => ({ 12 | type: types.FETCH_USERS, 13 | users 14 | }) 15 | 16 | const fetchFailure = (error) => ({ 17 | type: types.FETCH_FAILURE, 18 | error 19 | }) 20 | // 请求用户信息 21 | export const receiveUsers = (url) => async (dispatch) => { 22 | try { 23 | await dispatch(fetchRequset()) 24 | await get(url) 25 | .then( (response) => dispatch(fetchSuccess(response.message))) 26 | .cache((err) => dispatch(fetchFailure(err))) 27 | } catch (err) { 28 | dispatch( fetchFailure(err) ) 29 | } 30 | }; -------------------------------------------------------------------------------- /app/front/scss/header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 88px; 3 | background: #fff; 4 | border-bottom: 1px solid #eee; 5 | display: flex; 6 | justify-content: space-between; 7 | .title { 8 | height: 100%; 9 | line-height: 88px; 10 | padding-left: 16px; 11 | color: rgb(102, 102, 102); 12 | } 13 | .tabs-nav { 14 | ul { 15 | height: 100%; 16 | display: flex; 17 | justify-content: space-between; 18 | } 19 | li { 20 | height: 100%; 21 | line-height: 88px; 22 | } 23 | a { 24 | height: 100%; 25 | font-size: 16px; 26 | display: inline-block; 27 | padding: 0 5px 0 5px; 28 | margin-right: 10px; 29 | } 30 | .active { 31 | border-bottom: 2px solid #ff6600; 32 | color: #ff6600; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 简介 2 | > 根据目前个人水平,搭建的一个简易的后台博客系统 3 | --- 4 | ### 技术说明 5 | > - 前台使用的技术 *react* *redux* *react-router* 6 | > - UI组件库 *antd* 7 | > - 后台技术 *node* *express* *mongodb* 8 | > - 打包工具 *webpack2* 9 | --- 10 | #### 功能会慢慢的加 11 | > - 目前前台路由搭建完毕,后台路由设计完毕,后台结合webpack启动项目。。。 12 | > - 登录注册功能能可以使用 13 | > - 后台可以新增文章,获取用户信息,获取文章信息 14 | > - 首页增加后台入口 15 | #### 每天进步一点 16 | --- 17 | > - 未完成 登录session,加密,时间格式化等,管理员权限功能 18 | > - 文章在编辑,删除功能 19 | > - 前台请求后台文章数据,展示文章 20 | > - 完成了注册加密,登录解密 21 | 22 | #### 启动项目 23 | - git clone 24 | - npm install 25 | - mongod --dbpath /xxx(项目文件目录)/react-express-mongodb/app/server/db/ 或者 直接启动mongod(自行Google) 26 | - 前端启动 npm start 27 | - 后端启动 npm run server 28 | 29 | #### 我是小黑哥( Leo ),我为自己带盐 30 | 31 | 32 | > 1. Hello world 33 | > 2. QQ: 946732383 34 | > 3. [我的博客](http://www.cnblogs.com/heigehe/ "博客地址") 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/front/js/components/Admin/AdminLink.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/19. 3 | */ 4 | import React from 'react'; 5 | import { Row, Col } from 'antd'; 6 | import Partial from '../partial'; 7 | 8 | export default class AdminLink extends React.Component { 9 | render() { 10 | return ( 11 | 12 | 13 |

后台管理

14 | 15 | 16 | 24 | 25 |
26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /app/front/js/components/NavLink/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react'; 5 | // import { Icon } from 'antd'; 6 | import Partial from '../partial'; 7 | import '../../../scss/nav.scss'; 8 | 9 | export default class NavLink extends React.Component { 10 | render() { 11 | return ( 12 | 21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /app/front/js/reducers/articles.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 17:04:42 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-15 11:57:50 6 | */ 7 | // import { handleActions } from 'redux-actions'; 8 | import * as types from '../constants'; 9 | const initialState = { 10 | articles: [], 11 | isFetching: true 12 | } 13 | 14 | const articlesState = (state=initialState, action) => { 15 | switch (action.type) { 16 | case types.FETCH_REQUEST: 17 | return Object({}, state, {isFetching: true}) 18 | case types.FETCH_ARTICLES: 19 | let info = {}; 20 | if (action.article !== undefined) { 21 | info = action.article 22 | } 23 | return Object.assign({}, state, { articles: info, isFetching: false }); 24 | case types.FETCH_ADD_ARTICLE: 25 | return state; 26 | default: 27 | return state 28 | } 29 | } 30 | 31 | export default articlesState -------------------------------------------------------------------------------- /app/front/js/utils/enterOrLeaveRoute.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-13 14:09:04 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-19 21:41:02 6 | */ 7 | /* 8 | 路由 跳转 确认 集合 9 | 在这里处理组件进入和离开逻辑(通过路由的 onEnter onLeave 方法) 10 | */ 11 | 12 | // login 页面 离开时逻辑 13 | export const enterHomePage = (nextState, replace, next) => { 14 | /* 15 | 此处处理 进入 /home 路由的权限控制 16 | nextState : 表示跳转后的location 信息; 17 | replace 用于 更改下一个进入的页面地址,但是不会跳转; 18 | next : 用于跳转页面,没有其他操作则显示当前路由对应页面 19 | */ 20 | console.log('onEnter Home Page'); 21 | // 这里做判断, 如果不是管理员,之类的操作,进行页面跳转 22 | // replace({ pathname: '/' }) 23 | console.log(nextState); 24 | next(); 25 | } 26 | 27 | export const leaveHomePage = (nextLocation) => { 28 | // 返回 false 会继续停留当前页面, 29 | // 否则,返回一个字符串,会显示给用户,让其自己决定 30 | console.log('leave Home Page'); 31 | alert('确认离开吗'); 32 | // console.log(nextLocation); 33 | } 34 | -------------------------------------------------------------------------------- /app/front/scss/reset.scss: -------------------------------------------------------------------------------- 1 | /*公共样式--开始*/ 2 | html, body, div, ul, li, h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ol, form, input, textarea, th, td, select { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | *{box-sizing: border-box;} 7 | html, body { 8 | height: 100%; 9 | } 10 | 11 | body { 12 | font-family: "Microsoft YaHei"; 13 | font-size:14px; 14 | color:#333; 15 | background: rgb(243, 243, 243); 16 | } 17 | h1, h2, h3, h4, h5, h6{font-weight:normal;} 18 | ul,ol { 19 | list-style: none; 20 | } 21 | 22 | img { 23 | border: none; 24 | vertical-align: middle; 25 | } 26 | 27 | a { 28 | text-decoration: none; 29 | color: #232323; 30 | } 31 | 32 | a:hover, 33 | a:visited, 34 | a:active, 35 | a:focus { 36 | text-decoration: none; 37 | } 38 | 39 | table { 40 | border-collapse: collapse; 41 | table-layout: fixed; 42 | } 43 | 44 | input, textarea { 45 | outline: none; 46 | border: none; 47 | } 48 | 49 | textarea { 50 | resize: none; 51 | overflow: auto; 52 | } -------------------------------------------------------------------------------- /app/front/js/containers/Home/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React, { PropTypes } from 'react' 5 | import { connect } from 'react-redux' 6 | import PureRenderMixin from 'react-addons-pure-render-mixin'; 7 | 8 | import GetStorage from '../../components/GetStorage'; 9 | 10 | @connect( 11 | (state) => ({home: state.home}), 12 | (dispatch) => ({}) 13 | ) 14 | 15 | class HomeComponent extends React.Component { 16 | constructor(props, context) { 17 | super(props, context); 18 | this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); 19 | } 20 | static defaultProps = { 21 | home: {}, 22 | data: '' 23 | } 24 | 25 | static propTypes = { 26 | home: PropTypes.object, 27 | data: PropTypes.string 28 | } 29 | render() { 30 | return ( 31 |
32 |

{this.props.home.title}

33 |

{this.props.home.description}

34 |

{this.props.data}

35 | 编辑后台 36 |
37 | ); 38 | } 39 | } 40 | 41 | const Home = GetStorage('name')(HomeComponent); 42 | 43 | export default Home; 44 | -------------------------------------------------------------------------------- /app/front/js/store/store.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import { compose, createStore, applyMiddleware } from 'redux'; 5 | import thunkMiddleware from 'redux-thunk'; 6 | import { createLogger } from 'redux-logger'; 7 | // import { syncHistoryWithStore } from 'react-router-redux'; 8 | // import { browserHistory } from 'react-router'; 9 | // import { syncHistoryWithStore, routerMiddleware, push } from 'react-router-redux' 10 | 11 | import rootReducer from '../reducers' 12 | 13 | const logger = createLogger({ collapsed: true }); 14 | // const reduxRouterMiddleware = routerMiddleware(browserHistory); 15 | 16 | const createStoreWithMiddleware = compose( 17 | applyMiddleware( 18 | thunkMiddleware, 19 | // reduxRouterMiddleware, 20 | logger 21 | ), 22 | window.devToolsExtension ? window.devToolsExtension() : (f) => f)(createStore); 23 | 24 | const configureStore = (initialState) => { 25 | const store = createStoreWithMiddleware(rootReducer, initialState); 26 | if (module.hot) { 27 | module.hot.accept('../reducers', () => { 28 | const nextReducer = require('../reducers').default; 29 | store.replaceReducer(nextReducer); 30 | }); 31 | } 32 | return store; 33 | }; 34 | 35 | export default configureStore -------------------------------------------------------------------------------- /app/front/js/actions/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/20. 3 | */ 4 | import { browserHistory } from 'react-router'; 5 | import * as types from '../constants'; 6 | import { post } from '../utils/request'; 7 | 8 | const loginRequset = () => ({ 9 | type: types.FETCH_REQUEST 10 | }) 11 | 12 | const loginSuccess = (user) => ({ 13 | type: types.FETCH_USER_INFO, 14 | user 15 | }) 16 | 17 | const loginFailure = (error) => ({ 18 | type: types.FETCH_FAILURE, 19 | error 20 | }) 21 | // 请求register, login 22 | export const userFetch = (url, params, redirectUrl) => async (dispatch) => { 23 | try { 24 | await dispatch(loginRequset()); 25 | const response = await post(url, params); 26 | dispatch(loginSuccess(response)); 27 | if (response.status ==='01') { 28 | browserHistory.push(redirectUrl); 29 | } 30 | // await post(url, params) 31 | // .then( response => { 32 | // console.log(response); 33 | // dispatch(loginSuccess(response)); 34 | // if (response.status ==='01') { 35 | // browserHistory.push(redirectUrl); 36 | // } 37 | // }) 38 | // .cache(err => dispatch(loginFailure(err))) 39 | } catch (err) { 40 | dispatch( loginFailure(err) ) 41 | } 42 | }; 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/front/js/utils/request.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-13 14:07:50 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-15 10:52:20 6 | */ 7 | /* 8 | 封装请求 9 | */ 10 | // import fetch from 'isomorphic-fetch'; 11 | // import 'babel-polyfill'; 12 | require('es6-promise').polyfill(); 13 | require('isomorphic-fetch'); 14 | 15 | // baseUrl 由 webpack 根据 环境变量 更换 16 | // export const host = baseUrl; // 当前服务器地址 17 | const host = ''; // 当前服务器地址 18 | console.log(baseUrl); 19 | 20 | // 封装fetch 代替 ajax 请求数据 21 | const request = (method, url, body) => { 22 | method = method.toUpperCase(); 23 | if (method === 'GET') { 24 | body = undefined; 25 | } else { 26 | body = body && JSON.stringify(body); 27 | } 28 | 29 | return fetch(host + url, { 30 | method, 31 | headers: { 32 | 'Content-Type': 'application/json', 33 | 'Accept': 'application/json' 34 | }, 35 | body 36 | }).then((res) => { 37 | return res.json(); 38 | }).then((data) => { 39 | // console.log(data) 40 | return data 41 | }); 42 | } 43 | 44 | export const get = (url) => request('GET', url); 45 | export const post = (url, body) => request('POST', url, body); 46 | export const put = (url, body) => request('PUT', url, body); 47 | export const del = (url, body) => request('DELETE', url, body); 48 | -------------------------------------------------------------------------------- /app/front/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | 5 | import React from 'react'; 6 | import { render } from 'react-dom'; 7 | import { AppContainer } from 'react-hot-loader'; 8 | import { syncHistoryWithStore } from 'react-router-redux'; 9 | import { Provider } from 'react-redux'; 10 | // import { Router, Route, hashHistory, browserHistory, Link, IndexLink, IndexRoute } from 'react-router'; 11 | import { Router, browserHistory } from 'react-router'; 12 | import injectTapEventPlugin from 'react-tap-event-plugin'; 13 | 14 | import routes from './router'; 15 | import configureStore from './store' 16 | 17 | import '../scss/reset.scss' 18 | // import 'antd/lib/date-picker/style/css'; 19 | // import { DatePicker } from 'antd'; 20 | // react 的插件,提供onTouchTap() 21 | injectTapEventPlugin(); 22 | // 创建一个增强版的history来结合store同步导航事件 23 | // const store = configureStore(browserHistory); 24 | const store = configureStore(); 25 | const history = syncHistoryWithStore(browserHistory, store); 26 | 27 | // history.listen(location => analyticsService.track(location.pathname)) 28 | 29 | console.log(process.env.NODE_ENV); 30 | render( 31 | 32 | 33 | 34 | 35 | , 36 | document.getElementById('app') 37 | ); -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | /usr/local/bin/bower 9 | 10 | 11 | 12 | true 13 | 14 | false 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | General 26 | 27 | 28 | XPath 29 | 30 | 31 | 32 | 33 | AngularJS 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/front/js/containers/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react' 5 | // import { connect } from 'react-redux' 6 | // import { receiveUserInfo } from '../actions/about' 7 | import PureRenderMixin from 'react-addons-pure-render-mixin'; 8 | import Header from '../components/Header' 9 | import 'antd/dist/antd.less'; 10 | import '../../scss/app.scss'; 11 | import '../../scss/pubilc.scss' 12 | 13 | // import { Button } from 'antd'; 14 | 15 | // @connect( 16 | // state => ({about: state.about}), 17 | // dispatch => ({ receiveUserInfo }) 18 | // ) 19 | 20 | class App extends React.Component { 21 | constructor(props, context) { 22 | super(props, context); 23 | this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); 24 | } 25 | render() { 26 | // console.log(this.props.about); 27 | // console.log(this.props.receiveUserInfo); 28 | return ( 29 |
30 |
31 |
32 | {this.props.children} 33 |
34 |
35 | ); 36 | 37 | } 38 | } 39 | export default App 40 | // //将state.counter绑定到props的counter 41 | // const mapStateToProps = (state) => { 42 | // return { 43 | // counter: state.counter 44 | // } 45 | // }; 46 | // //将action的所有方法绑定到props上 47 | // const mapDispatchToProps = (dispatch, ownProps) => { 48 | // return { 49 | // increment: (...args) => dispatch(actions.increment(...args)), 50 | // decrement: (...args) => dispatch(actions.decrement(...args)) 51 | // } 52 | // }; 53 | // 54 | // //通过react-redux提供的connect方法将我们需要的state中的数据和actions中的方法绑定到props上 55 | // export default connect(mapStateToProps, mapDispatchToProps)(App) 56 | -------------------------------------------------------------------------------- /app/server/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-17 10:21:46 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-24 14:35:15 6 | */ 7 | 8 | // const crypto = require('crypto'); 9 | 10 | /* 11 | // 加密 12 | function encrypto(str, secret) { 13 | const cipher = crypto.createCipher('ase192', secret); 14 | let enc = cipher.update(str, 'utf8', 'hex'); 15 | enc += cipher.final('hex'); 16 | return enc; 17 | } 18 | 19 | // 解密 20 | function decrypto(str, secret) { 21 | const decipher = crypto.createDecipher('ase192', secret); 22 | let dec = decipher.update(str, 'hex', 'utf8'); 23 | dec += decipher.final('utf8'); 24 | return dec; 25 | } 26 | */ 27 | 28 | // 管理员权限 29 | const adminRequired = function (req, res, next) { 30 | if (!req.session.user) { 31 | return next(); 32 | } 33 | if (!req.session.user.isAdmin) { 34 | return next() 35 | } 36 | } 37 | 38 | // 需要登录 39 | const loginRequired = function(req, res, next) { 40 | if (!req.session || !req.session.user || req.session.user._id) { 41 | return next() 42 | } 43 | } 44 | 45 | // 判断是否登录 46 | const authUser = async (req, res, next) => { 47 | let currUser = null; 48 | if (!req.session.user) { 49 | return next() 50 | } 51 | const cookie = req.cookies['user-token']; 52 | if (!cookie) { 53 | req.session.user = null; 54 | return next(); 55 | } 56 | currUser = req.session.user; 57 | if (!currUser) { 58 | 59 | } 60 | 61 | 62 | } 63 | 64 | const noSession = (req, res) => { 65 | if(!req.session || req.session.user) { 66 | res.redirect('login'); 67 | return; 68 | } 69 | } 70 | 71 | module.exports = { 72 | adminRequired: adminRequired, 73 | loginRequired: loginRequired, 74 | authUser: authUser, 75 | noSession: noSession 76 | } -------------------------------------------------------------------------------- /app/front/js/router/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react'; 5 | // import { Router, Route, hashHistory, browserHistory, Link, IndexLink, IndexRoute, Redirect } from 'react-router'; 6 | import { Route, IndexRoute, Redirect } from 'react-router'; 7 | 8 | import About from '../components/About'; 9 | import Home from '../containers/Home'; 10 | import Login from '../containers/Login'; 11 | import Register from '../containers/Register'; 12 | // import Blog from '../components/Blog'; 13 | import App from '../containers/App'; 14 | import NotFound from '../components/NotFound' 15 | import { All, Auto, Mongo, Node, Blog } from '../components/Blog'; 16 | import { Admin, Users, AdminHome, Article, EditArticle } from '../components/Admin' 17 | import { enterHomePage, leaveHomePage } from '../utils/enterOrLeaveRoute' 18 | 19 | export default ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {/* 42 | 43 | */} 44 | 45 | ) -------------------------------------------------------------------------------- /app/front/js/actions/articles.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 16:53:31 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-20 16:59:07 6 | */ 7 | import { browserHistory } from 'react-router'; 8 | import { Modal } from 'antd'; 9 | import * as types from '../constants'; 10 | import { get, post } from '../utils/request'; 11 | 12 | const fetchRequset = () => ({ 13 | type: types.FETCH_REQUEST 14 | }) 15 | 16 | const fetchSuccess = (article) => ({ 17 | type: types.FETCH_ARTICLES, 18 | article 19 | }) 20 | 21 | const fetchAddArticle = (article) => ({ 22 | type: types.FETCH_ADD_ARTICLE, 23 | article 24 | }) 25 | 26 | const fetchFailure = (error) => ({ 27 | type: types.FETCH_FAILURE, 28 | error 29 | }) 30 | 31 | const success = () => { 32 | const modal = Modal.success({ 33 | title: '添加文章提示', 34 | content: '添加文章成功' 35 | }); 36 | setTimeout( () => { 37 | modal.destroy(); 38 | browserHistory.push('/admin/article'); 39 | }, 2500); 40 | } 41 | // 获取文章列表 42 | export const getArticles = (url) => async (dispatch) => { 43 | try { 44 | await dispatch(fetchRequset()) 45 | const data = await get(url); 46 | await dispatch(fetchSuccess(data.message)); 47 | } catch (err) { 48 | dispatch(fetchFailure(err)); 49 | } 50 | } 51 | // 新增文章 52 | export const addArticle = (url, params) => async (dispatch) => { 53 | try { 54 | await dispatch(fetchRequset()) 55 | const response = await post(url, params); 56 | if (response.status === '01') { 57 | success(); 58 | } 59 | dispatch(fetchAddArticle(response.message)); 60 | // await post(url, params) 61 | // .then( response => { 62 | // console.log(response); 63 | // dispatch(fetchAddArticle(response.message)); 64 | // if (response.status === '01') { 65 | // browserHistory.push('/admin/article'); 66 | // } 67 | // } 68 | // ).cache(err => dispatch(fetchFailure(err))) 69 | } catch (err) { 70 | dispatch( fetchFailure(err) ) 71 | } 72 | }; -------------------------------------------------------------------------------- /webpack/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/11. 3 | */ 4 | /** 5 | * 此文件主要定义一些配置常量 6 | * */ 7 | // 引入 node.js path 模块 8 | const path = require('path'); 9 | // sass-loader 的 配置 10 | exports.sassLoaderSuffix = '?outputStyle=expanded&sourceMap=true&sourceMapContents=true&includePaths[]=./node_modules'; 11 | exports.sassLoaderProd = '?outputStyle=expanded'; 12 | // 公共文件 13 | exports.vendor = [ 14 | 'react', 15 | 'react-dom', 16 | 'react-redux', 17 | 'react-router', 18 | 'react-router-redux', 19 | 'react-tap-event-plugin', 20 | 'redux', 21 | 'redux-thunk', 22 | 'isomorphic-fetch', 23 | 'es6-promise', 24 | 'pure-render-decorator', 25 | 'react-addons-css-transition-group', 26 | 'antd', 27 | 'antd/dist/antd.less' 28 | ]; 29 | // css 代码自动补全配置 30 | exports.autoConfig = { 31 | browsers: [ 32 | 'ie >= 9', 33 | 'ie_mob >= 10', 34 | 'ff >= 30', 35 | 'chrome >= 34', 36 | 'safari >= 7', 37 | 'opera >= 23', 38 | 'ios >= 7', 39 | 'android >= 4.4', 40 | 'bb >= 10' 41 | ], 42 | cascade: true, 43 | remove: true 44 | }; 45 | 46 | // js 压缩 配置 47 | exports.uglifyJsConfig = { 48 | beautify: false, // 不美化输出 49 | compress: { 50 | warnings: false, // 不保留警告 51 | drop_debugger: true, // 不保留调试语句 52 | drop_console: true // 不保留控制台输出信息 53 | }, 54 | mangle: { // 跳过这些,不改变命名 55 | except: ['$super', '$', 'exports', 'require'] 56 | }, 57 | space_colon: false, 58 | comments: false // 不保留注释 59 | }; 60 | 61 | // 定义 文件路径 注:文件在 根目录下的 webpack 文件夹下 62 | const ROOT_PATH = path.resolve(__dirname, '../'); 63 | 64 | exports.defPath = { 65 | ROOT_PATH: ROOT_PATH, 66 | APP_PATH: path.resolve(ROOT_PATH, 'app'), 67 | STATIC_PATH: path.resolve(ROOT_PATH, 'static'), 68 | DEV_PATH: path.resolve(ROOT_PATH, 'dev'), 69 | BUILD_PATH: path.resolve(ROOT_PATH, 'dist'), 70 | TPL_PATH: path.resolve(ROOT_PATH, 'static/tpl.html'), 71 | ENTRY_PATH: path.resolve(ROOT_PATH, 'app/front/js/index.js'), 72 | ESLINT_PATH: path.resolve(ROOT_PATH, './.eslintrc'), 73 | REQUEST_PATH: path.resolve(ROOT_PATH, 'app/front/js/utils/request') 74 | } -------------------------------------------------------------------------------- /app/front/js/components/Admin/Users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/19. 3 | */ 4 | import React, { Component, PropTypes } from 'react'; 5 | import { Table } from 'antd'; 6 | import { connect } from 'react-redux'; 7 | import { bindActionCreators } from 'redux'; 8 | 9 | require('es6-promise').polyfill(); 10 | require('isomorphic-fetch'); 11 | 12 | // const { ColumnGroup } = Table; 13 | 14 | import * as actions from '../../actions' 15 | @connect( 16 | (state) => ({users: state.users}), 17 | (dispatch) => bindActionCreators({...actions}, dispatch) 18 | ) 19 | 20 | export class Users extends Component { 21 | 22 | static propTypes = { 23 | users: PropTypes.object.isRequired, 24 | receiveUsers: PropTypes.func 25 | } 26 | 27 | constructor (props) { 28 | super(props) 29 | } 30 | 31 | componentDidMount() { 32 | this.props.receiveUsers('/api/admin/users'); 33 | } 34 | 35 | render() { 36 | const { isFetching, users } = this.props.users; 37 | const columns = [ 38 | { title: '用户ID', dataIndex: '_id', key: '_id' }, 39 | { title: '用户名', dataIndex: 'username', key: 'username' }, 40 | { title: '密码', dataIndex: 'password', key: 'password' }, 41 | { title: '是否是管理员', dataIndex: 'admin', key: 'admin' }, 42 | { title: '操作', 43 | dataIndex: '', 44 | key: 'x', 45 | render: () => 46 | 47 | 查看 48 | 49 | 删除 50 | 51 | } 52 | ]; 53 | if (!isFetching && users) { 54 | console.log(users); 55 | for (let i=0; i { 57 | if (key !== 'admin') { 58 | users[i].admin = users[i].isAdmin ? '是' : '否'; 59 | } 60 | }); 61 | } 62 | } 63 | return ( 64 |
65 | { 66 | (!isFetching && users) && users._id} style={{textAlign: 'center'}} bordered /> 67 | } 68 | 69 | ) 70 | } 71 | } -------------------------------------------------------------------------------- /webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by "苏萧" on 2017/7/11. 3 | */ 4 | const webpackMerge = require("webpack-merge"); 5 | const webpack = require("webpack"); 6 | const ChunkManifestPlugin = require("chunk-manifest-webpack-plugin"); 7 | const ExtractTextPlugin = require("extract-text-webpack-plugin");// 抽取 css,生成单独文件 8 | const WebpackChunkHash = require("webpack-chunk-hash"); 9 | 10 | const baseConfig = require("./webpack/base"); 11 | const config = require("./webpack/config"); 12 | const defPath = config.defPath; 13 | const APP_PATH = defPath.APP_PATH; 14 | 15 | module.exports = webpackMerge(baseConfig(), { 16 | entry: { 17 | app: defPath.ENTRY_PATH, 18 | vendor: config.vendor 19 | }, 20 | output: { 21 | path: defPath.BUILD_PATH, 22 | filename: "js/bundle.js?[chunkhash:10]", 23 | publicPath: "http://xxx.xxx", //修改发布地址 24 | chunkFilename: "chunk.js?[chunkhash:10]" 25 | }, 26 | modules: { 27 | rules: [ 28 | { 29 | test: /\.(scss|sass|css)$/, 30 | include: APP_PATH, 31 | use: ExtractTextPlugin.extract({ // css 单独打包,(2.x 改变很大) 32 | fallback: "style-loader", 33 | use: "css-loader!post-loader!sass-loader" + config.sassLoaderProd 34 | }) 35 | }, 36 | { 37 | test: /\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif|mp4|webm)(\?\S*)?$/, 38 | include: APP_PATH, 39 | loader: "url-loader?limit=8192&name=imgs/[name].[ext]?[hash:10]" 40 | } 41 | ] 42 | }, 43 | plugins: [ 44 | new webpack.HashedModeleIdPlugin(), 45 | new WebpackChunkHash(), // 标准webpack chunkhash 46 | new webpack.DefinePlugin({ 47 | "process.env.NODE_ENV": JSON.stringify("production") 48 | }), 49 | new webpack.optimize.UglifyJsPlugin(config.uglifyJsConfig), 50 | new webpack.optimize.CommonsChunkPlugin({ 51 | names: ["vendor", "manifest"], 52 | filename: "js/[name].js?[chunkhash:10]", 53 | minChunks: Infinity 54 | }), 55 | new ChunkManifestPlugin({ 56 | filename: "chunk-manifest.json", 57 | manifestVariable: "webpackManifest" 58 | }), 59 | //css抽取 60 | new ExtractTextPlugin({ 61 | filename: "css/styles.css?[contnethash:10]", 62 | disable: false, 63 | allChunks: true 64 | }) 65 | ] 66 | }); 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/server/api/register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/18. 3 | */ 4 | const express = require('express'); 5 | const bcrypt = require('bcrypt'); 6 | const router = express.Router(); 7 | // const mongoose = require('mongoose') 8 | 9 | const Users = require('../model/users'); 10 | 11 | // 统一返回格式 12 | 13 | let responseData; 14 | router.use( (req, res, next) => { 15 | responseData = { 16 | status: '01', 17 | message: '' 18 | } 19 | next(); 20 | }) 21 | 22 | router.post('/', (req, res, next) => { 23 | console.log(req.body); 24 | try { 25 | const username = req.body.username; 26 | const password = req.body.password; 27 | const repassword = req.body.repassword; 28 | if (username === '') { 29 | responseData = { 30 | status: '02', 31 | message: '用户名不能为空' 32 | } 33 | res.json(responseData); 34 | return; 35 | } 36 | // 密码不能为空 37 | if (password === '') { 38 | responseData.status = '03'; 39 | responseData.message = '请设置密码'; 40 | res.json(responseData); 41 | return; 42 | } 43 | // 两次输入的密码必须一致 44 | if (password !== repassword) { 45 | responseData.status = '04'; 46 | responseData.message = '两次输入的密码不一致'; 47 | res.json(responseData); 48 | return; 49 | } 50 | 51 | Users.findOne({username: username}, (err, user) => { 52 | console.log(req.body); 53 | if (err) { 54 | console.log(err) 55 | } else { 56 | if (user) { 57 | responseData.status = '05'; 58 | responseData.message = '用户名已经被注册了'; 59 | return res.json(responseData); 60 | } 61 | const saltRounds = 10; 62 | bcrypt.genSalt(saltRounds, (err, salt) => { 63 | bcrypt.hash(password, salt, (err, hash) => { 64 | if (err) { 65 | return next(err); 66 | } 67 | const newUser = new Users({ 68 | username: username, 69 | password: hash 70 | }); 71 | responseData = { 72 | status: '01', 73 | message: '注册成功' 74 | } 75 | newUser.save().then(() => { 76 | res.json(responseData); 77 | }); 78 | }) 79 | }) 80 | } 81 | }) 82 | } catch (err) { 83 | next(err) 84 | } 85 | 86 | }) 87 | 88 | module.exports = router; -------------------------------------------------------------------------------- /dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 48 | 49 | 50 | 51 | 52 | 53 | Leo 54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/front/js/components/Admin/EditArticle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-10-19 17:29:25 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-19 22:07:56 6 | */ 7 | import React from 'react'; 8 | import LzEditor from 'react-lz-editor'; 9 | 10 | export class EditArticle extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | htmlContent: '', 15 | markdownContent: '## HEAD 2 \n markdown examples \n ``` welcome ```', 16 | responseList: [] 17 | } 18 | this.receiveHtml = this.receiveHtml.bind(this); 19 | this.submitArticle = this.submitArticle.bind(this); 20 | } 21 | receiveHtml(content) { 22 | console.log('recieved HTML content', content); 23 | // this.setState({responseList:[]}); 24 | } 25 | submitArticle() { 26 | console.log(this.state.htmlContent); 27 | } 28 | render() { 29 | // let policy = ''; 30 | const uploadProps = { 31 | action: 'http://v0.api.upyun.com/devopee', 32 | onChange: this.onChange, 33 | listType: 'picture', 34 | fileList: this.state.responseList, 35 | data: (file) => { 36 | // console.log(file); 37 | }, 38 | multiple: true, 39 | beforeUpload: this.beforeUpload, 40 | showUploadList: true 41 | } 42 | const { htmlContent } = this.state; 43 | return ( 44 |
45 | 54 | {/* 55 | * markdown 实例 56 |
57 | 66 |
67 | */} 68 |
发布
69 |
70 | ) 71 | } 72 | } -------------------------------------------------------------------------------- /static/tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 48 | 49 | 50 | 51 | 52 | 53 | Leo Blog 54 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/front/js/components/Admin/Article.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-08-14 11:38:37 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-08-16 14:35:18 6 | */ 7 | 8 | import React, { Component, PropTypes } from 'react'; 9 | import { Table, Modal } from 'antd'; 10 | import { connect } from 'react-redux'; 11 | import { bindActionCreators } from 'redux'; 12 | 13 | const confirm = Modal.confirm; 14 | // const { ColumnGroup } = Table; 15 | 16 | import * as actions from '../../actions/articles' 17 | 18 | const success = () => { 19 | const modal = Modal.success({ 20 | title: '提示', 21 | content: '删除文章成功' 22 | }); 23 | setTimeout( () => { 24 | modal.destroy(); 25 | browserHistory.push('/admin/article'); 26 | }, 1500); 27 | } 28 | @connect( 29 | (state) => ({articles: state.articles}), 30 | (dispatch) => bindActionCreators({...actions}, dispatch) 31 | ) 32 | 33 | export class Article extends Component { 34 | constructor (props) { 35 | super(props); 36 | this.showConfirm = this.showConfirm.bind(this); 37 | } 38 | 39 | static propTypes = { 40 | getArticles: PropTypes.func, 41 | articles: PropTypes.object.isRequired 42 | } 43 | 44 | componentDidMount() { 45 | this.props.getArticles('/api/admin/article'); 46 | } 47 | 48 | showConfirm() { 49 | confirm({ 50 | title: '确认删除此文章?', 51 | content: '删除后无法复原', 52 | onOk() { 53 | console.log('OK'); 54 | success(); 55 | }, 56 | onCancel() { 57 | console.log('cancle'); 58 | } 59 | }) 60 | } 61 | 62 | render() { 63 | const { isFetching, articles } = this.props.articles; 64 | const columns = [ 65 | { title: '文章ID', dataIndex: '_id', key: '_id' }, 66 | { title: '文章分类', dataIndex: 'sort', key: 'sort' }, 67 | { title: '文章名称', dataIndex: 'title', key: 'title' }, 68 | { title: '文章内容', dataIndex: 'content', key: 'content' }, 69 | { title: '作者', dataIndex: 'author', key: 'author' }, 70 | { title: '创建时间', dataIndex: 'createDate', key: 'createDate' }, 71 | { title: '修改时间', dataIndex: 'updateDate', key: 'updateDate' }, 72 | 73 | { title: '操作', 74 | dataIndex: '', 75 | key: 'x', 76 | render: () => 77 | 78 | 编辑 79 | 80 | 删除 81 | 82 | } 83 | ]; 84 | return ( 85 |
86 | 87 | { 88 | !isFetching ?
users._id} style={{textAlign: 'center'}} bordered /> : null 89 | } 90 | 91 | ) 92 | } 93 | } -------------------------------------------------------------------------------- /app/front/js/containers/Login/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by '苏萧' on 2017/7/13. 3 | */ 4 | import React from 'react'; 5 | import PureRenderMixin from 'react-addons-pure-render-mixin'; 6 | 7 | import '../../../scss/login.scss'; 8 | // import Partial from '../partial'; 9 | import { Form, Icon, Input, Button, Checkbox } from 'antd'; 10 | import { connect } from 'react-redux'; 11 | import { bindActionCreators } from 'redux'; 12 | import { userFetch } from '../../actions/users'; 13 | const FormItem = Form.Item; 14 | 15 | @connect( 16 | (state) => ({userInfo: state.userInfo}), 17 | (dispatch) => bindActionCreators({userFetch}, dispatch) 18 | ) 19 | class LoginModel extends React.Component { 20 | constructor(props, context) { 21 | super(props, context); 22 | this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); 23 | } 24 | componentDidMount() { 25 | // To disabled submit button at the beginning. 26 | // this.props.form.validateFields(); 27 | } 28 | handleSubmit = (e) => { 29 | e.preventDefault(); 30 | this.props.form.validateFields((err, values) => { 31 | if (!err) { 32 | console.log('Received values of form: ', values); 33 | this.props.userFetch('/api/login', values, '/home'); 34 | } 35 | }); 36 | } 37 | render() { 38 | const { getFieldDecorator } = this.props.form; 39 | 40 | return ( 41 |
42 |
43 | 44 | {getFieldDecorator('username', { 45 | rules: [{ required: true, message: '请输入用户名' }] 46 | })( 47 | } placeholder="Username" /> 48 | )} 49 | 50 | 51 | {getFieldDecorator('password', { 52 | rules: [{ required: true, message: '请输入密码' }] 53 | })( 54 | } type="password" placeholder="Password" /> 55 | )} 56 | 57 | 58 | {getFieldDecorator('remember', { 59 | valuePropName: 'checked', 60 | initialValue: true 61 | })( 62 | 记住密码 63 | )} 64 | 忘记密码? 65 | 66 | 69 | 马上注册 70 | 71 | 72 |
73 | ); 74 | } 75 | } 76 | const Login = Form.create()(LoginModel); 77 | export default Login -------------------------------------------------------------------------------- /dev-server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: leo 3 | * @Date: 2017-10-20 17:24:33 4 | * @Last Modified by: leo 5 | * @Last Modified time: 2017-10-20 18:01:35 6 | */ 7 | const express = require('express'); 8 | const open = require('open'); 9 | const path = require('path') 10 | const webpack = require('webpack'); 11 | const WebpackDevMiddleware = require('webpack-dev-middleware'); 12 | const WebpackHotMiddleware = require('webpack-hot-middleware'); 13 | const bodyParser = require('body-parser'); 14 | const mongoose = require('mongoose'); 15 | const cookieParser = require('cookie-parser'); 16 | const cors = require('cors'); 17 | const session = require('express-session'); 18 | const MongoStore = require('connect-mongo/es5')(session); 19 | // const passport = require('passport'); 20 | 21 | const config = require('./webpack.dev.config'); 22 | const api = require('./app/server/api'); 23 | // const Users = require('./model/users'); 24 | 25 | const compiler = webpack(config); 26 | const port = 3333; 27 | const app = express(); 28 | // 结合webpack 29 | // 将这个添加到webpack配置文件的入口里面 ?reload=true 设置浏览器是否自动刷新; 30 | const hotMiddlewareScript = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=10000&reload=true'; 31 | const entries = Object.keys(config.entry); 32 | // 添加热加载信息 33 | entries.forEach((key) => { 34 | config.entry[key].push(hotMiddlewareScript); 35 | }) 36 | // 添加插件信息 37 | if(config.plugins === undefined) { 38 | config.plugins = [] 39 | } 40 | 41 | // 添加热加载插件 42 | config.plugins.push( 43 | new webpack.optimize.OccurrenceOrderPlugin(), 44 | new webpack.HotModuleReplacementPlugin(), 45 | new webpack.NoEmitOnErrorsPlugin() 46 | ) 47 | app.use(WebpackDevMiddleware(compiler, { 48 | publicPath: config.output.publicPath, 49 | quiet: true, 50 | stats: { colors: true } 51 | })); 52 | app.use(WebpackHotMiddleware(compiler, { 53 | log: () => {} 54 | })); 55 | 56 | // 文件位置 57 | app.use(express.static('static')); 58 | app.use(express.static('dist')); 59 | app.use(bodyParser.json()); 60 | 61 | // 登录验证 62 | app.use(cookieParser()); // 使用cookie-parser插件,后续代码直接使用req.cookies 63 | app.use(session({ 64 | store: new MongoStore({ 65 | mongooseConnection: mongoose.connection 66 | }), 67 | secret: 'leo-blog', 68 | resave: true, 69 | saveUninitialized: true, 70 | cookie: { 71 | path: '/' 72 | } 73 | })); 74 | 75 | // 设置跨域访问 76 | app.use(cors()); 77 | 78 | app.use('/api', api); 79 | // 解决子路由刷新无法访问的问题 80 | app.get('/*', (req, res, next) => { 81 | // console.log(req.session); 82 | const filename = path.join(config.output.path, 'index.html') 83 | console.log(filename); 84 | compiler.outputFileSystem.readFile(filename, (err, result) => { 85 | if (err) { 86 | return next(err) 87 | } 88 | res.set('content-type', 'text/html'); 89 | res.send(result); 90 | res.end(); 91 | }) 92 | }); 93 | // 连接mongodb 94 | mongoose.Promise = global.Promise; 95 | // 使用connect 不要使用createConnection 96 | mongoose.connect('mongodb://localhost:27017/leoBlog', {useMongoClient: true}, (err) => { 97 | if (err) { 98 | console.log(err) 99 | } else { 100 | console.log('==> 数据连接成功'); 101 | app.listen(port, (err) => { 102 | if (err) { 103 | console.log(err) 104 | } else { 105 | console.log(`正在打开==>http://localhost:${port}...`); 106 | open(`http://localhost:${port}`); 107 | } 108 | }) 109 | } 110 | }); 111 | 112 | 113 | -------------------------------------------------------------------------------- /app/server/api/admin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/19. 3 | */ 4 | const express = require('express'); 5 | const router = express.Router(); 6 | 7 | const Users = require('../model/users'); 8 | const Articles = require('../model/articles'); 9 | 10 | let responseData; 11 | 12 | router.use( (req, res, next) => { 13 | responseData = { 14 | status: '01', 15 | message: '' 16 | } 17 | next(); 18 | }); 19 | 20 | router.post('/', async (req, res, next) => { 21 | console.log(req.body); 22 | Users.find({}, (err, users) => { 23 | if (err) { 24 | console.log(err) 25 | } else { 26 | res.send('users') 27 | console.log(users); 28 | } 29 | }) 30 | // res.json({ 31 | // status: '0', 32 | // message: 'admin' 33 | // }) 34 | next(); 35 | }) 36 | 37 | router.get('/users', async (req, res, next) => { 38 | try { 39 | Users.find({}, (err, users) => { 40 | if (err) { 41 | console.log(err) 42 | } else { 43 | responseData.message = users; 44 | res.json(responseData); 45 | // res.send('users') 46 | console.log('-------users----------'); 47 | console.log(users); 48 | } 49 | // res.end(); 50 | // next(); 51 | }) 52 | } catch (err) { 53 | console.log('-------users2222----------'); 54 | next(err) 55 | } 56 | 57 | }) 58 | // 获取文章 59 | router.get('/article', async (req, res, next) => { 60 | try { 61 | Articles.find({}, (error, article) => { 62 | if (error) { 63 | console.log(error) 64 | } else { 65 | responseData.message = article; 66 | res.json(responseData); 67 | console.log('-----------article-----------'); 68 | console.log(article); 69 | console.log('-----------article-----------'); 70 | } 71 | }) 72 | } catch(err) { 73 | next(err); 74 | } 75 | }) 76 | // 新增文章 77 | router.post('/article/add', async (req, res, next) => { 78 | const id = req.body._id; 79 | console.log('-----------article-----------'); 80 | const newArtilce = new Articles(req.body); 81 | console.log(newArtilce); 82 | try { 83 | Articles.find({_id: id}, (err, article) => { 84 | if (err) { 85 | console.log(err) 86 | } else { 87 | if (article.length === 0) { 88 | newArtilce.save().then( (ret) => { 89 | console.log(ret); 90 | responseData.message = ret; 91 | res.json(responseData); 92 | }, (error) => { 93 | responseData.message = error; 94 | res.json(responseData) 95 | }) 96 | } 97 | } 98 | }) 99 | } catch(err) { 100 | next(err); 101 | } 102 | }) 103 | // 更新文章 104 | router.put('/article/update/:id', async (req, res, next) => { 105 | try { 106 | Articles.findOneAndUpdate({_id: req.params.id}, req.body, (error, article) => { 107 | if (error) { 108 | console.log(error) 109 | } else { 110 | res.json(article); 111 | console.log(article); 112 | } 113 | }) 114 | } catch(err) { 115 | next(err); 116 | } 117 | }) 118 | 119 | // 删除文章 120 | 121 | router.post('/article/delete/:id', async (req, res, next) => { 122 | try { 123 | Articles.findOneAndRemove({_id: req.params.id}, req.body, (error, article) => { 124 | if (error) { 125 | console.log(error) 126 | } else { 127 | res.json(article); 128 | console.log(article); 129 | } 130 | }) 131 | } catch(err) { 132 | next(err); 133 | } 134 | }) 135 | 136 | module.exports = router; -------------------------------------------------------------------------------- /app/front/js/components/Admin/AdminHome.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by suxiao on 2017/7/24. 3 | */ 4 | import React, { Component } from 'react'; 5 | import { Form, Input, Button, Select } from 'antd'; 6 | const FormItem = Form.Item; 7 | const { Option } = Select; 8 | const { TextArea } = Input; 9 | import { connect } from 'react-redux'; 10 | import { bindActionCreators } from 'redux'; 11 | 12 | // const { ColumnGroup } = Table; 13 | 14 | import * as actions from '../../actions/articles' 15 | 16 | @connect( 17 | (state) => ({articles: state.articles}), 18 | (dispatch) => bindActionCreators({...actions}, dispatch) 19 | ) 20 | 21 | class Home extends Component { 22 | constructor() { 23 | super(); 24 | this.state = { 25 | formLayout: 'horizontal' 26 | }; 27 | } 28 | handleSubmit = (e) => { 29 | e.preventDefault(); 30 | this.props.form.validateFieldsAndScroll((err, values) => { 31 | if (!err) { 32 | console.log('Received values of form: ', values); 33 | this.props.addArticle('/api/admin/article/add', values); 34 | // if (!this.props.articles.isFetching) { 35 | // success(); 36 | // } 37 | // const promise = new Promise( (respones, reject) => { 38 | // this.props.addArticle('/api/admin/article/add', values); 39 | // if (true) { 40 | // resolve(value) 41 | // } 42 | // }); 43 | // // this.props.addArticle('/api/admin/article/add', values); 44 | // promise.then( (value) => { 45 | // console.log(value); 46 | // }) 47 | } else { 48 | console.log(err); 49 | } 50 | }); 51 | } 52 | render() { 53 | const { formLayout } = this.state; 54 | const { getFieldDecorator } = this.props.form; 55 | const formItemLayout = formLayout === 'horizontal' ? { 56 | labelCol: { span: 4 }, 57 | wrapperCol: { span: 14 } 58 | } : null; 59 | const buttonItemLayout = formLayout === 'horizontal' ? { 60 | wrapperCol: { span: 14, offset: 4 } 61 | } : null; 62 | return ( 63 |
64 | 65 |
66 | 70 | {getFieldDecorator('sort', { 71 | rules: [{ required: true, message: '请选择文章分类' }] 72 | })( 73 | 81 | )} 82 | 83 | 87 | {getFieldDecorator('title', { 88 | rules: [{ required: true, message: '请输入标题' }] 89 | })( 90 | 91 | )} 92 | 93 | 97 | {getFieldDecorator('content', { 98 | rules: [{ required: true, message: '请输入文章内容' }] 99 | })( 100 |