Простое управление пользователями Node-React-Koa, добавляйте, удаляйте, изменяйте и проверяйте небольшую демонстрацию

внешний интерфейс сервер JavaScript React.js

Передняя часть: приложение create-реагировать и реагировать на аксиомы

Серверная часть: node koa, продолжение mysql

Как новичок в интерфейсе, после попытки поработать над интерфейсом реакции в течение определенного периода времени, я хочу попробовать написать веб-сервисы с узлом, притворяясь очень мощным. После просмотра туториала по узлу + экспресс-туториала + туториала по koa я начал готовиться к самостоятельному написанию небольшого демо.

Внешний рендеринг (реакция + antd + create-react-app)

адрес гитхаба:node-react-koa

добавить изменить

удалять

Простая страница списка пользователей с запросом вверху, добавлением пользовательских кнопок, удалением, редактированием пользовательских кнопок в списке и разбиением на страницы внизу.

Дизайн пользовательской таблицы в базе данных

(1) Передняя конструкция

1. Используйте леса Create-React-App, официально разработанные Facebook для создания фронтальной структуры RACT.

(1) Глобально установить приложение create-реагировать

npm install -g create-react-app

(2) Создать проект

create-react-app node-react-koa
cd node-react-koa && mkdir server //node服务都放在该文件下
npm run eject //可省略,只为了看配置 config
npm start

Из этого каталога проекта, как показано ниже

(3) Создание интерфейсных страниц

1. Установите antd, высококачественный компонент React, который работает «из коробки».antd design
npm install antd --save 
2. УстановкаaxiosHTTP-библиотека на основе обещаний, которая работает в браузерах и node.js.
npm install axios --save
Поскольку это небольшая демонстрация, я рисую страницу прямо в src/App.js.
src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
import { Table, Pagination, Input, Row, Button, Modal, Form } from 'antd';
import 'antd/dist/antd.css'
const { Search } = Input;
const FormItem = Form.Item;
const { confirm } = Modal;
class App extends Component {
  constructor(props) {
    super(props);
  }
  columns = [{
    dataIndex: "username", title: "用户",
  }, {
    dataIndex: "age", title: "年龄",
  }, {
    dataIndex: "address", title: "地址"
  }, {
    dataIndex: "action", title: "操作", width: 200, render: (text, row) => {
      return <div>
        <Button onClick={() => this.modal('edit', row)} >编辑</Button>
        <Button style={{ marginLeft: 10 }} type="danger" onClick={() => this.remove(row)} >删除</Button>
      </div>
    }
  }];
  state = {
    dataSource: [{ username: "slf", age: "18", address: "杭州", id: 1 }],
    current: 1,
    size: 10,
    total: 1,
    visible: false,
    modalType: "add"
  }
  componentDidMount() {
    this.sizeChange(this.state.current,this.state.size);
  }
  //分页
  sizeChange = (current, size) => {
  //todo
  }
  //提交
  handleOk = () => {
  //todo 
  }
  //添加编辑用户
  modal = (type, row) => {
    this.setState({
      visible: true,
      modalType: type
    }, () => {
      this.props.form.resetFields();
      if (type === 'add') return;
      this.props.form.setFieldsValue({
        username: row.username,
        age: row.age,
        address: row.address
      })
    })
  }
  remove = (row) => {
    confirm({
      title: '是否要删除该用户?',
      okText: '是',
      okType: '否',
      cancelText: 'No',
      onOk() {
        //todo
      },
      onCancel() {
        //todo
      },
    });
  }
  render() {
    const { getFieldDecorator } = this.props.form;
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 4 },
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 },
      },
    };
    return (
      <div className="App">
        <Row>
          <Search style={{ width: 300 }} />
          <Button type="primary" style={{ marginLeft: 20 }} onClick={() => this.modal('add')} >添加用户</Button>
        </Row>
        <Row style={{ paddingTop: 20 }}>
          <Table dataSource={this.state.dataSource} rowKey={row => row.id} bordered columns={this.columns} pagination={false} />
        </Row>
        <Row style={{ paddingTop: 20 }}>
          <Pagination
            showTotal={(total) => `共 ${total} 条`}
            current={this.state.current} total={this.state.total} pageSize={this.state.size}
            onChange={this.sizeChange} />
        </Row>
        <Modal
          title={this.state.modalType === 'add' ? "添加用户" : "编辑用户"}
          onOk={this.handleOk}
          onCancel={() => this.setState({ visible: false })}
          visible={this.state.visible}
        >
          <Form>
            <FormItem label="用户"  {...formItemLayout}>
              {getFieldDecorator('username', {
                rules: [{ required: true, message: 'Please input your username!' }],
              })(
                <Input placeholder="Username" />
              )}
            </FormItem>
            <FormItem label="年龄"  {...formItemLayout}>
              {getFieldDecorator('age', {
                rules: [{ required: true, message: 'Please input your age!' }],
              })(
                <Input placeholder="age" />
              )}
            </FormItem>
            <FormItem label="地址"  {...formItemLayout}>
              {getFieldDecorator('address', {
                rules: [{ required: true, message: 'Please input your address!' }],
              })(
                <Input placeholder="address" />
              )}
            </FormItem>
          </Form>
        </Modal>
      </div >
    );
  }
}
export default Form.create()(App);
Задача в приведенном выше коде — это место для отладки с серверной службой (идеальная версия внешнего интерфейса вставляется позже).

(2) Внутреннее строительство

В бэкэнд-конструкции я использовалkoaа такжеsequelize

база данных mysql

koa — среда веб-разработки следующего поколения на платформе Node.js.

Sequelize — спящий режим на стороне JS, который завершает операции CRUD со стороны сервера в базу данных.

1. Установите зависимости

npm install koa koa-body koa-cors koa-router sequelize mysql2 --save
koa-bodyПотому что веб-приложения не могут обойтись без обработки форм (таких как пользовательские формы добавления и редактирования). По сути, формы представляют собой пары ключ-значение, которые отправляются на сервер методом POST. Модуль koa-body можно использовать для извлечения пар ключ-значение из тела запроса POST.
koa-corsРешение междоменных проблем
koa-routerсопоставление обработчика URL

2. Подготовка

Создайте следующее в каталоге сервера:

/server/app.jsзапустить режим запуска файлаnode server/app.js
/server/routersПуть API внешнего доступа
/server/modelУровень данных: соединение с базой данных index.js, пользовательская таблица user.js

3. Создайте новый коа-сервис

/server/app.js

const Koa = require('koa');
const cors = require('koa-cors');
const router = require('./routers/index')
// 创建一个Koa对象表示web app本身:
const app = new Koa();
app.use(cors());//解决跨域问题
// 对于任何请求,app将调用该异步函数处理请求:
app.use(async (ctx, next) => {
    console.log(ctx.request.path + ':' + ctx.request.method);
    await next();
});
app.use(router.routes());
app.listen(3005);
console.log('app started at port 3005...');

4. Подключиться к базе данных

/server/model/index.js

operatorAliases должны писать true, иначе последующее использование sql вызовет проблемы, например, использование нечеткого запроса $like вызовет проблему Invalid value
const Sequelize = require('sequelize');
const sequelize = new Sequelize('数据库名', '用户名', '密码', {
    host: 'localhost',
    dialect: 'mysql',
    operatorsAliases: true,
    pool: {
        max: 5, min: 0, acquire: 30000, idle: 10000
    },
    define: {
        timestamps: false,
    },
})
sequelize
    .authenticate()
    .then(() => {
        console.log('Connection has been established successfully.');
    })
    .catch(err => {
        console.error('Unable to connect to the database:', err);
    });
module.exports = sequelize;

5. Таблица пользователей

Оформление по таблице

/server/model/user.js

sequelizeНажмите, чтобы узнать, как использовать

/server/model/user.js

const Sequelize = require('sequelize')
const sequelize = require('./index')
const User = sequelize.define('userinfos', {
    id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, unique: true },
    username: { type: Sequelize.STRING },
    age:{type:Sequelize.INTEGER},
    address: { type: Sequelize.STRING },
    isdelete: { type: Sequelize.INTEGER, allowNull: true }//软删除 0为未删除,1为删除
});
module.exports = User;

Приходят следующие моменты, в центре внимания этой статьи, напишите сервер! ! ! ! !

Поскольку проект небольшой, я написал его в каталоге маршрутизаторов.

/routers/index.js

1. Ввести для использованияkoa-body, koa-router, таблица данных (модель/user.js)

const koaBody = require('koa-body');
const router = require('koa-router')();
const User = require('../model/user');

2. Первый интерфейс API: получить список всех пользователей

router.get('/users', async (ctx, next) => {
    const user = await User.findAll({
        where: { isdelete: 0 },
    })
    ctx.body = user;
});

бегать

node server/app.js

почтальон тест

успех!

Формальные добавления, удаления и исправления начинаются ниже.

1. Увеличение числа пользователей Сначала вернитесь к src/App.js и улучшите метод отправки добавления.

handleOk = () => {
        this.props.form.validateFieldsAndScroll((err, value) => {
            if (err) return;
            let data = {
                username: value.username, age: value.age, address: value.address
            };
            if (this.state.modalType === 'add') {
                axios.post("http://127.0.0.1:3005/user", data)
                    .then(msg => {
                        this.sizeChange(this.state.current, this.state.size);
                        this.setState({visible: false});
                        message.success('success!')
                    })
            } else {
                axios.put("http://127.0.0.1:3005/user/" + this.state.editRow.id, data)
                    .then(data => {
                        this.sizeChange(this.state.current, this.state.size);
                        this.setState({visible: false});
                        message.success('success!')
                    })
            }
        })
    }

2. Добавить API сервер/маршрутизаторы/index.js

router.post('/user', koaBody(), async (ctx) => {
    const user = await User.build(ctx.request.body).save();
    ctx.body = user;
})

3. Редактируйте пользователей таким же образом сервер/маршрутизаторы/index.js

router.put('/user/:id', koaBody(), async (ctx) => {
    const body = ctx.request.body;
    const user = await User.findById(ctx.params.id);
    await user.update({...body})
    ctx.body = user;
})

4. Удалить пользователя сервер/маршрутизатор/index.js

router.delete('/user/:id', async (ctx) => {
    const user = await User.findById(ctx.params.id).then((user) => user);
    user.isdelete = 1;
    await user.save();
    ctx.body = { success: true }
})

5. Пейджинговый запрос сервер/маршрутизатор/index.js

//{"limit":10,"offset":0,"search":"slf"}
router.post('/user-search', koaBody(), async (ctx) => {
    const body = ctx.request.body;
    const user = await User.findAndCount({
        where: {
            isdelete: 0, username: {
                $like: `%${body.search}%`
            }
        },
        limit: body.limit,
        offset: body.offset
    });
    ctx.body = user;
});

наконец

module.exports = router;

Идеальный передок

import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
import {Table, Pagination, Input, Row, Button, Modal, Form, message} from 'antd';
import 'antd/dist/antd.css'

const {Search} = Input;
const FormItem = Form.Item;
const {confirm} = Modal;

class App extends Component {
    constructor(props) {
        super(props);
    }

    columns = [{
        dataIndex: "username", title: "用户",
    }, {
        dataIndex: "age", title: "年龄",
    }, {
        dataIndex: "address", title: "地址"
    }, {
        dataIndex: "action", title: "操作", width: 200, render: (text, row) => {
            return <div>
                <Button onClick={() => this.modal('edit', row)}>编辑</Button>
                <Button style={{marginLeft: 10}} type="danger" onClick={() => this.remove(row)}>删除</Button>
            </div>
        }
    }];
    state = {
        dataSource: [],
        current: 1,
        size: 10,
        total: 0,
        visible: false,
        modalType: "add",
        search: "",
        editRow: {}
    }

    componentDidMount() {
        this.sizeChange(this.state.current, this.state.size);
    }

    //分页
    sizeChange = (current, size) => {
        let data = {
            search: this.state.search,
            limit: size,
            offset: (parseInt(current) - 1) * size
        }
        axios.post("http://localhost:3005/user-search", data).then(data => {
            this.setState({
                dataSource: data.data.rows,
                total: data.data.count,
                current, size
            })
        })
    };
    //提交
    handleOk = () => {
        this.props.form.validateFieldsAndScroll((err, value) => {
            if (err) return;
            let data = {
                username: value.username, age: value.age, address: value.address
            };
            if (this.state.modalType === 'add') {
                axios.post("http://127.0.0.1:3005/user", data)
                    .then(msg => {
                        this.sizeChange(this.state.current, this.state.size);
                        this.setState({visible: false});
                        message.success('success!')
                    })
            } else {
                axios.put("http://127.0.0.1:3005/user/" + this.state.editRow.id, data)
                    .then(data => {
                        this.sizeChange(this.state.current, this.state.size);
                        this.setState({visible: false});
                        message.success('success!')
                    })
            }
        })
    }
    //添加编辑用户
    modal = (type, row) => {
        this.setState({
            visible: true,
            modalType: type
        }, () => {
            this.props.form.resetFields();
            if (type === 'add') return;
            this.props.form.setFieldsValue({
                username: row.username,
                age: row.age,
                address: row.address
            })
            this.setState({editRow: row})
        })
    }
    remove = (row) => {
        let _this = this;
        confirm({
            title: '是否要删除该用户?',
            okText: '是',
            okType: '否',
            cancelText: 'No',
            onOk() {
                axios.delete("http://127.0.0.1:3005/user/"+row.id)
                    .then(data=>{
                        _this.sizeChange(_this.state.current, _this.state.size);
                        message.success('success!')
                    })
            }
        });
    };
    search = (name) => {
        this.setState({
            search: name
        }, () => {
            this.sizeChange(1, 10)
        })
    };

    render() {
        const {getFieldDecorator} = this.props.form;
        const formItemLayout = {
            labelCol: {
                xs: {span: 24},
                sm: {span: 4},
            },
            wrapperCol: {
                xs: {span: 24},
                sm: {span: 16},
            },
        };
        return (
            <div className="App">
                <Row>
                    <Search style={{width: 300}} onChange={this.search}/>
                    <Button type="primary" style={{marginLeft: 20}} onClick={() => this.modal('add')}>添加用户</Button>
                </Row>
                <Row style={{paddingTop: 20}}>
                    <Table dataSource={this.state.dataSource} rowKey={row => row.id} bordered columns={this.columns}
                           pagination={false}/>
                </Row>
                <Row style={{paddingTop: 20}}>
                    <Pagination
                        showTotal={(total) => `共 ${total} 条`}
                        current={this.state.current} total={this.state.total} pageSize={this.state.size}
                        onChange={this.sizeChange}/>
                </Row>
                <Modal
                    title={this.state.modalType === 'add' ? "添加用户" : "编辑用户"}
                    onOk={this.handleOk}
                    onCancel={() => this.setState({visible: false})}
                    visible={this.state.visible}
                >
                    <Form>
                        <FormItem label="用户"  {...formItemLayout}>
                            {getFieldDecorator('username', {
                                rules: [{required: true, message: 'Please input your username!'}],
                            })(
                                <Input placeholder="username"/>
                            )}
                        </FormItem>
                        <FormItem label="年龄"  {...formItemLayout}>
                            {getFieldDecorator('age', {
                                rules: [{required: true, message: 'Please input your age!'}],
                            })(
                                <Input placeholder="age"/>
                            )}
                        </FormItem>
                        <FormItem label="地址"  {...formItemLayout}>
                            {getFieldDecorator('address', {
                                rules: [{required: true, message: 'Please input your address!'}],
                            })(
                                <Input placeholder="address"/>
                            )}
                        </FormItem>
                    </Form>
                </Modal>
            </div>
        );
    }
}

export default Form.create()(App);

Резюме: я действительно хорош (бесстыжий)