├── .editorconfig
├── .eslintrc
├── .gitignore
├── .roadhogrc.js
├── .roadhogrc.mock.js
├── .webpackrc
├── Dockerfile
├── README.md
├── data.json
├── mock
└── .gitkeep
├── package-lock.json
├── package.json
├── public
└── index.html
├── routers
└── hero.js
├── server
└── index.js
├── src
├── assets
│ ├── bg.jpg
│ └── yay.jpg
├── components
│ ├── DeleteConfirm.js
│ ├── Example.js
│ ├── Header.jsx
│ └── Public.less
├── index.css
├── index.js
├── models
│ └── hero.js
├── router.js
├── routes
│ ├── detail
│ │ ├── Detail.jsx
│ │ └── Detail.less
│ ├── list
│ │ ├── List.jsx
│ │ └── List.less
│ └── login
│ │ ├── Login.jsx
│ │ └── Login.less
├── services
│ ├── example.js
│ └── hero.js
└── utils
│ ├── axios.js
│ └── request.js
├── vmodels
└── heroSchema.js
└── web
├── Dockerfile
├── dist
├── index.css
├── index.html
├── index.js
└── static
│ └── bg.7ea45f27.jpg
└── nginx
└── default.conf
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "umi"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 |
7 | # misc
8 | .DS_Store
9 | npm-debug.log*
10 |
--------------------------------------------------------------------------------
/.roadhogrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | var pxtorem = require('postcss-pxtorem');
3 | const svgSpriteDirs = [
4 | require.resolve('antd').replace(/warn\.js$/, ''), // antd-mobile 内置svg
5 | path.resolve(__dirname, 'src/assets'), // 业务代码本地私有 svg 存放目录
6 | ];
7 | export default {
8 | entry: "src/index.js",
9 | disableCSSModules: true,
10 | publicPath: "./",
11 | less: true,
12 | env: {
13 | development: {
14 | extraBabelPlugins: [
15 | "dva-hmr",
16 | "transform-runtime",
17 | ["import", { "libraryName": "antd-mobile", 'libraryDirectory': 'lib', "style": true }]
18 | ],
19 | extraPostCSSPlugins: [
20 | pxtorem({
21 | rootValue: 100,
22 | propWhiteList: [],
23 | }),
24 | ],
25 | },
26 | production: {
27 | extraBabelPlugins: [
28 | "transform-runtime",
29 | ["import", { "libraryName": "antd-mobile", 'libraryDirectory': 'lib', "style": true }]
30 | ],
31 | extraPostCSSPlugins: [
32 | pxtorem({
33 | rootValue: 100,
34 | propWhiteList: [],
35 | }),
36 | ]
37 | }
38 | },
39 |
40 | autoprefixer: {
41 | "browsers": [
42 | "iOS >= 8", "Android >= 4"
43 | ]
44 | },
45 | svgSpriteLoaderDirs: svgSpriteDirs,
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/.roadhogrc.mock.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 | };
4 |
--------------------------------------------------------------------------------
/.webpackrc:
--------------------------------------------------------------------------------
1 | {
2 | "extraBabelPlugins": [
3 | ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
4 | ],
5 | "proxy":{
6 | "/":{
7 | "target":"http://localhost:10005",
8 | "changeOrigin": true
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12.16.3
2 | RUN mkdir -p /home/project
3 | WORKDIR /home/project
4 | COPY . /home/project
5 | RUN npm install
6 | EXPOSE 50002
7 | CMD ["npm", "run", "server"]
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mongo-react
2 | ## preview
3 | 
4 | 
5 | 
6 | 
7 |
--------------------------------------------------------------------------------
/mock/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/mock/.gitkeep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start": "roadhog server",
5 | "build": "roadhog build",
6 | "server": "node app.js",
7 | "lint": "eslint --ext .js src test",
8 | "precommit": "npm run lint"
9 | },
10 | "dependencies": {
11 | "antd": "^3.10.8",
12 | "axios": "^0.18.0",
13 | "babel-plugin-import": "^1.11.0",
14 | "body-parser": "^1.18.2",
15 | "dva": "^2.4.1",
16 | "express": "^4.16.2",
17 | "mongoose": "^5.0.10",
18 | "nodemon": "^1.18.4",
19 | "react": "^16.2.0",
20 | "react-dom": "^16.2.0",
21 | "require_optional": "^1.0.1"
22 | },
23 | "devDependencies": {
24 | "babel-plugin-dva-hmr": "^0.3.2",
25 | "eslint": "^4.14.0",
26 | "eslint-config-umi": "^0.1.1",
27 | "eslint-plugin-flowtype": "^2.34.1",
28 | "eslint-plugin-import": "^2.6.0",
29 | "eslint-plugin-jsx-a11y": "^5.1.1",
30 | "eslint-plugin-react": "^7.1.0",
31 | "husky": "^0.12.0",
32 | "redbox-react": "^1.4.3",
33 | "less": "^2.7.3",
34 | "roadhog": "^2.0.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LOL 英雄列表
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/routers/hero.js:
--------------------------------------------------------------------------------
1 |
2 | //引入express模块
3 | const express = require("express");
4 | //定义路由级中间件
5 | const router = express.Router();
6 | //引入数据模型模块
7 | const Hero = require("../vmodels/heroSchema");
8 |
9 | // 查询所有英雄信息路由
10 | router.get("/hero", (req, res) => {
11 | // console.log('========',req.query.pageSize)
12 | // console.log('+++++++++++++',Hero.count())
13 | var total = 0;
14 | Hero.count({}, function(err, count){
15 | if(err) return;
16 | total = count;
17 | res.set('x-header', total)
18 | })
19 |
20 | Hero.find({})
21 | .limit(Math.min(parseInt(req.query.pageSize) || 10, 100))
22 | .skip(parseInt(req.query.currentPage -1) * req.query.pageSize || 0)
23 | .sort({ updatedAt: -1 })
24 | .then(heros => {
25 | res.json(heros);
26 | })
27 | .catch(err => {
28 | res.json(err);
29 | })
30 |
31 | });
32 |
33 | // 通过ObjectId查询单个英雄信息路由
34 | router.get("/hero/:id", (req, res) => {
35 | Hero.findById(req.params.id)
36 | .then(hero => {
37 | res.json(hero);
38 | })
39 | .catch(err => {
40 | res.json(err);
41 | });
42 | });
43 |
44 | // 添加一个英雄信息路由
45 | router.post("/hero", (req, res) => {
46 | //使用Hero model上的create方法储存数据
47 | Hero.create(req.body, (err, hero) => {
48 | if (err) {
49 | res.json(err);
50 | } else {
51 | res.json(hero);
52 | }
53 | });
54 | });
55 |
56 | //更新一条英雄信息数据路由
57 | router.put("/hero/:id", (req, res) => {
58 | Hero.findOneAndUpdate(
59 | { _id: req.params.id },
60 | {
61 | $set: {
62 | name: req.body.name,
63 | nickname: req.body.nickname,
64 | sex: req.body.sex,
65 | address: req.body.address,
66 | dowhat: req.body.dowhat,
67 | favourite: req.body.favourite,
68 | explain: req.body.explain
69 | }
70 | },
71 | {
72 | new: true
73 | }
74 | )
75 | .then(hero => res.json(hero))
76 | .catch(err => res.json(err));
77 | });
78 |
79 | // 添加图片路由
80 | router.put("/addpic/:id", (req, res) => {
81 | Hero.findOneAndUpdate(
82 | { _id: req.params.id },
83 | {
84 | $push: {
85 | imgArr: req.body.url
86 | }
87 | },
88 | {
89 | new: true
90 | }
91 | )
92 | .then(hero => res.json(hero))
93 | .catch(err => res.json(err));
94 | });
95 | // 编辑图片
96 | router.put("/editpic/:id", (req, res) => {
97 | Hero.findOneAndUpdate(
98 | { _id: req.params.id },
99 | {
100 | $set: {
101 | imgArr: req.body.imgArr
102 | }
103 | },
104 | {
105 | new: true
106 | }
107 | )
108 | .then(hero => res.json(hero))
109 | .catch(err => res.json(err));
110 | });
111 |
112 | //删除一条英雄信息路由
113 | router.delete("/hero/:id", (req, res) => {
114 | Hero.findOneAndRemove({
115 | _id: req.params.id
116 | })
117 | .then(hero => res.send(`${hero.name}删除成功`))
118 | .catch(err => res.json(err));
119 | });
120 |
121 | module.exports = router;
122 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const hero = require('../routers/hero')
3 | const mongoose = require("mongoose");
4 | const path = require('path')
5 | const fs = require('fs')
6 | const bodyParser = require("body-parser")
7 |
8 |
9 |
10 |
11 | //这一句是连接上数据库
12 | // var db = mongoose.connect('mongodb://127.0.0.1:27017/reactTest',{ useNewUrlParser: true });
13 | // console.log('db:===',db)
14 | // # mongodb 为协议
15 | // # james: 连接数据库的用户
16 | // # 123456: 该用户的密码
17 | // # localhost: 本地的地址(因为这是本地环境)
18 | // # 27017: mongodb的端口号(这个一般是默认值,也可以进行修改)
19 | // # example: 数据库的名字
20 | // var db = 'mongodb://root:1234@192.168.56.101:27017/reactTest?authMechanism=SCRAM-SHA-1'
21 |
22 | // ......
23 | // mongoose.connect(db,{useUnifiedTopology: true, useNewUrlParser: true});
24 | mongoose.connect("mongodb://localhost:27017/reactTest", {
25 | useNewUrlParser: true
26 | });
27 |
28 |
29 | const app = express()
30 | app.use(bodyParser.json());
31 | app.use(bodyParser.urlencoded({ extended: false }));
32 | app.use('/api',hero)
33 | // app.use(express.static(path.resolve(__dirname, './dist')))
34 | // 首页静态页面
35 | // app.get('*', function(req, res) {
36 | // const html = fs.readFileSync(path.resolve(__dirname, './dist/index.html'), 'utf-8')
37 | // res.send(html)
38 | // })
39 | app.all('*', function(req, res, next) {
40 | res.header("Access-Control-Allow-Origin", "*");
41 | res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
42 | res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
43 | next();
44 | });
45 | // 监听80端口
46 | app.listen(50002);
47 | console.log('server is running 50002');
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/assets/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/src/assets/bg.jpg
--------------------------------------------------------------------------------
/src/assets/yay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/src/assets/yay.jpg
--------------------------------------------------------------------------------
/src/components/DeleteConfirm.js:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 | import { Modal } from 'antd';
4 | import styles from './Public.less';
5 |
6 | function DeleteConfirm({
7 | content = '确认删除吗?',
8 | ...modalProps
9 | }) {
10 | const modalOpts = {
11 | ...modalProps,
12 | width: 610,
13 | };
14 |
15 | return (
16 |
17 |
18 | {content}
19 |
20 |
21 | );
22 | }
23 |
24 | export default DeleteConfirm;
25 |
--------------------------------------------------------------------------------
/src/components/Example.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Example = () => {
4 | return (
5 |
6 | Example
7 |
8 | );
9 | };
10 |
11 | Example.propTypes = {
12 | };
13 |
14 | export default Example;
15 |
--------------------------------------------------------------------------------
/src/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'dva/router';
3 | import { Icon, Layout, Menu, Dropdown } from 'antd';
4 | const { Header } = Layout;
5 | const menu = (
6 |
20 | );
21 | class HeaderLayout extends React.Component {
22 | constructor(props) {
23 | super(props);
24 | this.state = {
25 | username: ''
26 | };
27 | }
28 | componentDidMount() {
29 | const username = sessionStorage.getItem('guest');
30 | this.setState({
31 | username
32 | })
33 | }
34 | render() {
35 | return (
36 |
47 | );
48 | }
49 | };
50 |
51 | HeaderLayout.propTypes = {
52 | };
53 |
54 | export default HeaderLayout;
55 |
--------------------------------------------------------------------------------
/src/components/Public.less:
--------------------------------------------------------------------------------
1 | .modal {
2 | margin: 15px 0 15px 35px;
3 | color: rgba(37,48,57,0.60);
4 | text-align: center;
5 | }
6 |
7 | :global(.vertical-center-modal) {
8 | text-align: center;
9 | white-space: nowrap;
10 | :global(.ant-modal) {
11 | display: inline-block;
12 | vertical-align: middle;
13 | top: 0;
14 | text-align: left;
15 | }
16 | }
17 | :global(.vertical-center-modal:before) {
18 | content: '';
19 | display: inline-block;
20 | height: 100%;
21 | vertical-align: middle;
22 | width: 0;
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 |
2 | html, body, :global(#root) {
3 | height: 100%;
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import dva from 'dva';
2 | import { message } from 'antd';
3 | import './index.css';
4 |
5 | // 1. Initialize
6 | const app = dva({
7 | onError(e, dispatch) {
8 | message.error(`${e.message}` || '接口错误');
9 | },
10 | });
11 |
12 | // 2. Plugins
13 | // app.use({});
14 |
15 | // 3. Model
16 | app.model(require('./models/hero').default);
17 |
18 | // 4. Router
19 | app.router(require('./router').default);
20 |
21 | // 5. Start
22 | app.start('#root');
23 |
--------------------------------------------------------------------------------
/src/models/hero.js:
--------------------------------------------------------------------------------
1 | import { get_heros, post_hero, delete_hero, put_heros, put_add_pic, get_hero_detail, put_edit_pic } from '../services/hero';
2 | import queryString from 'query-string';
3 | export default {
4 | namespace: 'Hero',
5 |
6 | state: {
7 | detail: {},
8 | },
9 |
10 | subscriptions: {
11 | setup({ dispatch, history }) {
12 | // eslint-disable-line
13 | console.log(history)
14 | history.listen((location) => {
15 | const pathname = location.pathname;
16 | let { search } = location;
17 | const query = queryString.parse(search);
18 | console.log('query: ', query);
19 | if(pathname === '/detail') {
20 | dispatch({
21 | type: 'get_hero_detail', //在model内不需要加namespace,如果在外面用则需要加
22 | payload: query.id
23 | })
24 | }
25 | })
26 | },
27 | },
28 |
29 | effects: {
30 | *get_heros({ payload, callback }, { call }) {
31 | const data = yield call(get_heros, payload);
32 | callback(data);
33 | },
34 | *post_hero({ payload, callback }, { call }) {
35 | const response = yield call(post_hero, payload);
36 | const data = response.data;
37 | callback(data);
38 | },
39 | *delete_hero({ payload, callback }, { call }) {
40 | const response = yield call(delete_hero, payload);
41 | const data = response.data;
42 | callback(data);
43 | },
44 | *put_heros({ payload, callback }, { call }) {
45 | const response = yield call(put_heros, payload);
46 | const data = response.data;
47 | callback(data);
48 | },
49 | *put_add_pic({ payload, callback }, { call }) {
50 | const response = yield call(put_add_pic, payload);
51 | const data = response.data;
52 | callback(data);
53 | },
54 | *put_edit_pic({ payload, callback }, { call }) {
55 | const response = yield call(put_edit_pic, payload);
56 | const data = response.data;
57 | callback(data);
58 | },
59 | *get_hero_detail({ payload, callback }, { call, put }) {
60 | yield put({ type: 'queryHeroDetail', payload: { detail: {} } });
61 | const data = yield call(get_hero_detail, payload);
62 | console.log('detail:', data.data)
63 | if (data) {
64 | yield put({
65 | type: 'queryHeroDetail',
66 | payload: {
67 | detail: data.data
68 | },
69 | })
70 | }
71 | }
72 | },
73 | reducers: {
74 | queryHeroDetail(state, action) {
75 | return { ...state, ...action.payload, ...action.detail };
76 | }
77 | }
78 | };
79 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Router, Route, Switch } from 'dva/router';
3 | import Login from './routes/login/Login.jsx';
4 | import List from './routes/list/List.jsx';
5 | import Detail from './routes/detail/Detail.jsx';
6 |
7 | function RouterConfig({ history }) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default RouterConfig;
20 |
--------------------------------------------------------------------------------
/src/routes/detail/Detail.jsx:
--------------------------------------------------------------------------------
1 | import Header from './../../components/Header.jsx';
2 | import React from 'react';
3 | import { connect } from 'dva';
4 |
5 | import { Carousel, Button } from 'antd';
6 | import { Link } from 'dva/router';
7 |
8 | class Detail extends React.Component {
9 | constructor(props) {
10 | super(props);
11 | this.state = {
12 | // imgArr: [],
13 | // name: '',
14 | // favourite: '',
15 | // explain: ''
16 | };
17 | }
18 |
19 | UNSAFE_componentWillMount() {
20 | // console.log(this.props)
21 | // const { location, dispatch } = this.props;
22 | // console.log('location: ', location);
23 | // console.log('dispatch: ', dispatch);
24 | // let id = location.pathname.split('/')[2];
25 | // dispatch({
26 | // type: 'heroModel/get_hero_detail',
27 | // payload: id,
28 | // callback: (res) => {
29 | // console.log(res);
30 | // this.setState({
31 | // imgArr: res.imgArr,
32 | // explain: res.explain,
33 | // favourite: res.favourite,
34 | // name: res.name
35 | // })
36 | // }
37 | // })
38 | }
39 | render() {
40 | const { Hero } = this.props;
41 | // console.log('Hero: ', Hero);
42 | // console.log('Hero: ', Hero.detail);
43 | let imgArr, explain, favourite, name;
44 | if (Hero && Object.keys(Hero.detail).length !== 0) {
45 | imgArr = Hero.detail.imgArr;
46 | explain = Hero.detail.explain;
47 | favourite = Hero.detail.favourite;
48 | name = Hero.detail.name;
49 | }
50 | return (
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | { imgArr ? imgArr.map((item, index) => {
61 | return (
62 |
63 |

69 |
70 | );
71 | })
72 | : ''}
73 |
74 |
75 | {name} ******* {favourite}
76 |
77 |
被动:
78 |
{explain}
79 |
80 |
81 | );
82 | }
83 | }
84 | Detail.propTypes = {};
85 | function mapStateToProps({ Hero }) {
86 | return { Hero };
87 | }
88 | export default connect(mapStateToProps)(Detail);
89 |
--------------------------------------------------------------------------------
/src/routes/detail/Detail.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/src/routes/detail/Detail.less
--------------------------------------------------------------------------------
/src/routes/list/List.jsx:
--------------------------------------------------------------------------------
1 | import Header from './../../components/Header.jsx';
2 | import React from 'react';
3 | import { connect } from 'dva';
4 | import { Link } from 'dva/router';
5 |
6 | import { Table, Button, Layout, message, Modal, Drawer, Form, Col, Row, Input, Select, Tag } from 'antd';
7 |
8 | const { Option } = Select;
9 | const { Content } = Layout;
10 | const confirm = Modal.confirm;
11 | class List extends React.Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | collapsed: false,
16 | visible: false,
17 | imgVisible: false,
18 | loading: true,
19 | tableData: [],
20 | total: 0,
21 | imgUrl: '',
22 | imgId: '',
23 | editForm: {},
24 | pagination: {
25 | currentPage: 1,
26 | pageSize: 10,
27 | },
28 | };
29 | }
30 | toggleCollapsed = () => {
31 | this.setState({
32 | collapsed: !this.state.collapsed,
33 | });
34 | };
35 | showDrawer = () => {
36 | this.setState({
37 | visible: true,
38 | types: 1
39 | });
40 | };
41 | onClose = () => {
42 | this.setState({
43 | visible: false,
44 | });
45 | };
46 | // pic
47 | showImgDrawer = (id) => {
48 | console.log(id);
49 | this.setState({
50 | imgVisible: true,
51 | imgId: id,
52 | })
53 | };
54 |
55 | imgClose = () => {
56 | this.setState({
57 | imgVisible: false,
58 | });
59 | };
60 |
61 | handleChange = (value) => {
62 | console.log(`Selected: ${value}`);
63 | }
64 | onShowSizeChange = (current, pageSize) => {
65 | console.log(current, pageSize);
66 | }
67 | componentDidMount() {
68 | this.getData();
69 | }
70 | componentDidUpdate() {
71 | if(this.state.visible === false) {
72 | this.props.form.resetFields();
73 | }
74 | }
75 | //编辑
76 | editHandle = (obj) =>{
77 | console.log('obj: ', obj);
78 | this.setState({
79 | visible: true,
80 | types: 2,
81 | editForm: obj
82 | });
83 | this.props.form.setFieldsValue({
84 | name: obj.name,
85 | nickname: obj.nickname,
86 | explain: obj.explain,
87 | favourite: obj.favourite,
88 | sex: obj.sex,
89 | dowhat: obj.dowhat,
90 | address: obj.address
91 | });
92 | }
93 | ImputChange = (e) => {
94 | // console.log(e.target.value);
95 | this.setState({
96 | imgUrl: e.target.value
97 | })
98 | }
99 | // 添加图片
100 | addPicHandle = () => {
101 | let that = this;
102 | const { imgUrl, imgId } = that.state;
103 | const { dispatch } = this.props;
104 | console.log(imgUrl,imgId)
105 | if (!imgUrl.trim()) {
106 | message.info('图片格式不正确');
107 | } else {
108 | dispatch({
109 | type: 'Hero/put_add_pic',
110 | payload: {
111 | id: imgId,
112 | url: imgUrl
113 | },
114 | callback: (res) => {
115 | message.success('添加成功');
116 | this.setState({
117 | imgUrl: '',
118 | imgId: '',
119 | imgVisible: false
120 | })
121 | }
122 | })
123 | }
124 | }
125 | // 添加
126 | handleSubmit = (e) => {
127 | let that = this;
128 | const { dispatch } = this.props;
129 | const { types, editForm } = this.state;
130 | e.preventDefault();
131 | if(types === 2){
132 | this.props.form.validateFields((err, values) => {
133 | values = Object.assign(editForm, values);
134 | console.log('values: ', values);
135 | if (!err) {
136 | // message.loading('正在添加...');
137 | dispatch({
138 | type: 'Hero/put_heros',
139 | payload: values,
140 | callback: (res) => {
141 | message.success('修改成功');
142 | this.setState({
143 | visible: false,
144 | editId: ''
145 | });
146 | this.props.form.resetFields();
147 | that.getData();
148 | }
149 | })
150 | }
151 | });
152 | } else {
153 | this.props.form.validateFields((err, values) => {
154 | if (!err) {
155 | // message.loading('正在添加...');
156 | dispatch({
157 | type: 'Hero/post_hero',
158 | payload: values,
159 | callback: (res) => {
160 | message.success('添加成功');
161 | this.setState({
162 | visible: false,
163 | });
164 | this.props.form.resetFields();
165 | that.getData();
166 | }
167 | })
168 | }
169 | });
170 | }
171 | }
172 | // 删除
173 | showDeleteConfirm = (id) => {
174 | let that = this;
175 | const { dispatch } = this.props;
176 | confirm({
177 | title: '此操作将永久删除该文件, 是否继续?',
178 | okText: '确定',
179 | okType: 'primary',
180 | cancelText: '取消',
181 | onOk() {
182 | dispatch({
183 | type: 'Hero/delete_hero',
184 | payload: id,
185 | callback: (res) => {
186 | message.success('删除成功');
187 | that.getData();
188 | }
189 | })
190 | console.log(id)
191 | },
192 | onCancel() {
193 | console.log('Cancel');
194 | },
195 | });
196 | }
197 | // 获取所有
198 | getData = () => {
199 | const { dispatch } = this.props;
200 | const { pagination, payload } = this.state;
201 | const query = {
202 | ...pagination,
203 | ...payload
204 | }
205 | dispatch({
206 | type: 'Hero/get_heros',
207 | payload: query,
208 | callback: res =>{
209 | this.setState({
210 | tableData: res.data,
211 | // eslint-disable-next-line
212 | total: parseInt(res.headers['x-header'])
213 | })
214 | }
215 | })
216 | this.setState({
217 | loading: false
218 | })
219 | }
220 | // 翻页
221 | handleTableChange = (pagination, filters, sorter) => {
222 | console.log(pagination);
223 | // let that = this;
224 | const { pageSize, current } = pagination;
225 | const { dispatch } = this.props;
226 | const { payload } = this.state;
227 | this.setState({
228 | pagination: {
229 | currentPage: current,
230 | pageSize,
231 | },
232 | });
233 | const query = {
234 | ...payload,
235 | pageSize,
236 | currentPage: current
237 | };
238 | dispatch({
239 | type: 'Hero/get_heros',
240 | payload: query,
241 | callback: res => {
242 | this.setState({
243 | tableData: res.data,
244 | // eslint-disable-next-line
245 | total: parseInt(res.headers['x-header'])
246 | })
247 | }
248 | });
249 | }
250 | render() {
251 | const { getFieldDecorator } = this.props.form;
252 | const { loading, tableData, total, pagination, types } = this.state;
253 | const columns = [
254 | {
255 | title: '英雄',
256 | key: 'nickname',
257 | dataIndex: 'nickname',
258 | width: 150,
259 | },
260 | {
261 | title: '名字',
262 | key: 'name',
263 | dataIndex: 'name',
264 | width: 100,
265 | },
266 | {
267 | title: '性别',
268 | key: 'sex',
269 | dataIndex: 'sex',
270 | render: sex => (sex === 'man' ? '汉子' : '妹子'),
271 | width: 80,
272 | },
273 | {
274 | title: '籍贯',
275 | key: 'address',
276 | dataIndex: 'address',
277 | render: address => (address === '0' ? '艾欧尼亚' : address === '1' ? '祖安' : address === '2' ? '雷瑟守备' : address === '3'? '裁决之地' : '黑色玫瑰'),
278 | width: 150,
279 | },
280 | {
281 | title: '位置',
282 | key: 'dowhat',
283 | dataIndex: 'dowhat',
284 | render: dowhat => (
285 |
286 | {dowhat.map(tag => {tag === '0' ? '上单': tag === '1'? '中单': tag === '2'? '下路': tag === '3'? '辅助': '打野' })}
287 | {/* {dowhat.map(tag => {
288 | tag === '0'? 上单 : tag === '1'? 中单 : tag === '2'? 下路 : tag === '3'? 辅助 : 打野
289 | })} */}
290 |
291 | ),
292 | width: 180,
293 | },
294 | {
295 | title: '台词',
296 | key: 'favourite',
297 | dataIndex: 'favourite',
298 | width: 250,
299 | },
300 | {
301 | title: '操作',
302 | width: 350,
303 | // fixed: 'right',
304 | render: (record, obj) => (
305 |
306 | {
307 |
308 |
309 |
310 |
311 |
315 |
322 |
327 |
328 | }
329 |
330 | ),
331 | },
332 | ];
333 | return (
334 |
335 |
336 |
337 |
338 |
339 |
342 |
343 | (总页数: {total}), }} />
357 |
358 |
359 | {/* 编辑添加 */}
360 |
372 |
462 |
475 |
483 |
487 |
488 |
489 | {/* 添加图片 */}
490 |
497 |
498 |
499 | {this.state.imgList}
500 |
501 |
514 |
522 |
525 |
526 |
527 |
528 | );
529 | }
530 | }
531 | const ListInfo = Form.create()(List);
532 | List.propTypes = {};
533 | function mapStateToProps({ Hero }) {
534 | return { Hero };
535 | }
536 | export default connect(mapStateToProps)(ListInfo);
537 |
--------------------------------------------------------------------------------
/src/routes/list/List.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/src/routes/list/List.less
--------------------------------------------------------------------------------
/src/routes/login/Login.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'dva';
3 | import { Form, Icon, Input, Button } from 'antd';
4 | import { Link, routerRedux } from 'dva/router';
5 | import './Login.less';
6 | import bg from '../../assets/bg.jpg';
7 | const FormItem = Form.Item;
8 |
9 | class NormalLoginForm extends React.Component {
10 | state = {
11 | userName: 'guest',
12 | password: 'guest'
13 | }
14 | handleSubmit = (e) => {
15 | const { dispatch } = this.props;
16 | e.preventDefault();
17 | this.props.form.validateFields((err, values) => {
18 | if (!err) {
19 | dispatch(routerRedux.replace('/list'));
20 | console.log('Received values of form: ', values);
21 | sessionStorage.setItem('guest', values.userName)
22 | }
23 | });
24 | }
25 | render() {
26 | const { getFieldDecorator } = this.props.form;
27 | const { userName, password } = this.state;
28 | const sty = {
29 | backgroundImage: `url(${bg})`,
30 | backgroundSize: 'cover',
31 | height: '100%',
32 | width: '100%',
33 | position: 'relative'
34 | }
35 | return (
36 |
37 |
60 |
61 | );
62 | }
63 | }
64 |
65 | const Login = Form.create()(NormalLoginForm);
66 |
67 | export default connect()(Login);
68 |
--------------------------------------------------------------------------------
/src/routes/login/Login.less:
--------------------------------------------------------------------------------
1 | .login-form{
2 | background: red;
3 | }
4 |
--------------------------------------------------------------------------------
/src/services/example.js:
--------------------------------------------------------------------------------
1 | import axios from '../utils/axios'
2 |
3 | // 获取英雄列表
4 | export async function get_heros(params) {
5 | return axios.get('/api/hero', { params });
6 | }
7 |
8 | // 编辑英雄信息
9 | export async function put_heros(params) {
10 | return axios.put(`/api/hero/${params._id}`, params);
11 | }
12 |
13 | // 添加新英雄
14 | export async function post_hero(params) {
15 | return axios.post(`/api/hero`, params);
16 | }
17 |
18 | // 删除英雄
19 | export async function delete_hero(params) {
20 | return axios.delete(`/api/hero/${params}`, params)
21 | }
22 |
23 | // 英雄添加图片
24 | export async function put_add_pic(params) {
25 | return axios.put(`/api/addpic/${params.id}`, params)
26 | }
27 |
28 | // 获取单个英雄详情
29 | export async function get_hero_detail(params) {
30 | return axios.get(`/api/hero/${params}`, params)
31 | }
32 |
--------------------------------------------------------------------------------
/src/services/hero.js:
--------------------------------------------------------------------------------
1 | import axios from '../utils/axios'
2 |
3 | // 获取英雄列表
4 | export async function get_heros(params) {
5 | return axios.get('/api/hero', { params });
6 | }
7 |
8 | // 编辑英雄信息
9 | export async function put_heros(params) {
10 | return axios.put(`/api/hero/${params._id}`, params);
11 | }
12 |
13 | // 添加新英雄
14 | export async function post_hero(params) {
15 | return axios.post(`/api/hero`, params);
16 | }
17 |
18 | // 删除英雄
19 | export async function delete_hero(params) {
20 | return axios.delete(`/api/hero/${params}`, params)
21 | }
22 |
23 | // 英雄添加图片
24 | export async function put_add_pic(params) {
25 | return axios.put(`/api/addpic/${params.id}`, params)
26 | }
27 | // 英雄b编辑图片
28 | export async function put_edit_pic(params) {
29 | return axios.put(`/api/editpic/${params.id}`, params)
30 | }
31 |
32 | // 获取单个英雄详情
33 | export async function get_hero_detail(params) {
34 | return axios.get(`/api/hero/${params}`, params)
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/axios.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | // axios 配置
4 | var instance = axios.create({
5 | // 本地测试地址:
6 | // baseURL: 'http://localhost:8000',
7 | // 线上地址:
8 | // baseURL: 'http://144.34.148.126:3000',
9 | timeout: 5000
10 | })
11 |
12 | // 可以在这先申明错误代码表示的含义
13 |
14 | // 添加请求拦截器
15 | instance.interceptors.request.use(config => {
16 | // 在发送请求之前做些什么
17 | // config.data = JSON.stringify(config.data)
18 | config.headers = {
19 | 'Content-Type': 'application/json'
20 | }
21 | return config
22 | }, error => {
23 | // 对请求错误做些什么
24 | console.log(error) // for debug
25 | return Promise.reject(error)
26 | })
27 |
28 | // 添加响应拦截器
29 | instance.interceptors.response.use(response => {
30 | // 对响应数据做点什么
31 | // const res = response.data
32 | // 对错误代码做处理
33 | return response
34 | }, error => {
35 | // 对响应错误做点什么
36 | console.log('err' + error) // for debug
37 | return Promise.reject(error)
38 | })
39 |
40 | export default instance
41 |
42 | /**
43 | * post 请求方法
44 | * @param url
45 | * @param data
46 | * @returns {Promise}
47 | */
48 | export function post (url, data = {}) {
49 | return new Promise((resolve, reject) => {
50 | instance.post(url, data).then(response => {
51 | resolve(response.data)
52 | }, err => {
53 | reject(err)
54 | })
55 | })
56 | }
57 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import fetch from 'dva/fetch';
2 |
3 | function parseJSON(response) {
4 | return response.json();
5 | }
6 |
7 | function checkStatus(response) {
8 | if (response.status >= 200 && response.status < 300) {
9 | return response;
10 | }
11 |
12 | const error = new Error(response.statusText);
13 | error.response = response;
14 | throw error;
15 | }
16 |
17 | /**
18 | * Requests a URL, returning a promise.
19 | *
20 | * @param {string} url The URL we want to request
21 | * @param {object} [options] The options we want to pass to "fetch"
22 | * @return {object} An object containing either "data" or "err"
23 | */
24 | export default function request(url, options) {
25 | return fetch(url, options)
26 | .then(checkStatus)
27 | .then(parseJSON)
28 | .then(data => ({ data }))
29 | .catch(err => ({ err }));
30 | }
31 |
--------------------------------------------------------------------------------
/vmodels/heroSchema.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const heroSchema = mongoose.Schema({
4 | name :String,
5 | nickname : String,
6 | sex : String,
7 | address : String,
8 | dowhat : [],
9 | imgArr:[],
10 | favourite:String,
11 | explain:String,
12 | createdAt : { type : Date, default : Date.now },
13 | }, { collection: 'myhero', timestamps: true})
14 | //这里mongoose.Schema要写上第二个参数,明确指定到数据库中的哪个表取数据
15 |
16 |
17 | const Hero = module.exports = mongoose.model('hero',heroSchema);
18 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 | COPY dist/ /usr/share/nginx/html/
3 | COPY nginx/default.conf /etc/nginx/conf.d/default.conf
4 |
--------------------------------------------------------------------------------
/web/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LOL 英雄列表
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/web/dist/static/bg.7ea45f27.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengheai/mongo-react/665ed3208064b65f8787d7e765dcd13588e1211c/web/dist/static/bg.7ea45f27.jpg
--------------------------------------------------------------------------------
/web/nginx/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 10001;
3 | server_name localhost;
4 |
5 | #charset koi8-r;
6 | # access_log /var/log/nginx/host.access.log main;
7 | # error_log /var/log/nginx/error.log error;
8 |
9 | location / {
10 | root /usr/share/nginx/html;
11 | index index.html index.htm;
12 | }
13 |
14 | location /api {
15 | proxy_pass http://192.168.56.101:50002;
16 | index index.html; # 设置默认网页
17 | }
18 |
19 | #error_page 404 /404.html;
20 |
21 | # redirect server error pages to the static page /50x.html
22 | #
23 | error_page 500 502 503 504 /50x.html;
24 | location = /50x.html {
25 | root /usr/share/nginx/html;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------