作者可能是本意想要做一个图书管理系统,不过添加书籍的时候报错,所以简单的页面我们简单的看看
先上github地址:https://github.com/hesisi/react-bookManager
看package.json文件是有用到中间件的,心里多了点期待,也许帮它修复好bug也行呢
先看页面
很简单,可能只是框架的效果吧
看看代码
//srcindex.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
// 引入中间件
import {Provider} from 'react-redux';
// reducer
import bookReducer from './reducers/bookReducer';
// userreducer
import userReducer from './reducers/userReducer';
import { createStore ,applyMiddleware ,combineReducers } from 'redux';
import thunk from 'redux-thunk';
const rootReducer = combineReducers({
bookReducer,
userReducer
});
//store
const store = createStore(rootReducer,applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
store.subscribe(() => {
console.log("================监听store变化:"+JSON.stringify(store));
});
registerServiceWorker();
//app.js中定义了几个去不同页面的路由
import React from 'react';
import ReactDOM from 'react-dom';
import HomeLayout from './layouts/HomeLayout';
import BookAdd from './components/BookAdd';
import BookListContainer from './containers/BookList';
import UserAdd from './components/UserAdd';
import UserListContainer from './components/UserList';
import FileAdd from './components/FileAdd';
import FileList from './components/FileList';
import { BrowserRouter as Router ,Route } from 'react-router-dom';
import registerServiceWorker from './registerServiceWorker';
{/*
store的数据结构
store = {
bookReducer : {
data : [{},{}]
},
userReducer : {
data : [{},{}]
}
}
*/}
class App extends React.Component{
render (){
return (
<Router>
<HomeLayout>
<div>
<Route path="/book/add" component={BookAdd}></Route>
<Route path="/book/list" component={BookListContainer}></Route>
<Route path="/user/add" component={UserAdd} ></Route>
<Route path="/user/list" component={UserListContainer}></Route>
<Route path="/file/add" component={FileAdd}></Route>
<Route path="/file/list" component={FileList}></Route>
</div>
</HomeLayout>
</Router>
);
}
}
export default App;
接下来我们根据路由看一下页面
//user/list
//应该是数据部分请求没有接口,所以没有数据渲染出来
import React from 'react';
import { Table ,Button ,Popconfirm ,Divider ,Modal} from 'antd';
import {initUserAction} from "../actions/userActions";
import PropTypes from 'prop-types';
import FormLayout from './Form';
import SearchInput from '../components/SearchInput';
class UserList extends React.Component{
constructor(props){
super(props);
this.state = {
title : "",
visible : false,
confirmLoading : false,
formData : {},
operation : ""
};
}
componentWillMount(){
const {store} = this.context;
fetch("http://localhost:3001/user")
.then(res => res.json())
.then(res => {
store.dispatch(initUserAction(res));
});
}
//点击编辑
editHandle(record){
//record:{"id":10002,"name":"PHP从入门到死亡","price":89,"owner_id":10002}
this.setState({
title : "修改",
visible : true,
formData : record,
operation : "edit" //编辑状态
});
}
//在子组件中点击添加需要调用的props函数
addHandle(){
this.setState({
title : "添加",
visible : true,
operation : "add"
});
}
//Form表单点击确定的时候要执行的props函数,动态获取Input组件的值
comfirmHandle(data){
this.setState({
visible : false,
formData : data
})
let { operation } = this.state;
const { formData } = this.state;
if(operation === "edit"){
this.props.editBook(formData);
}else{
this.props.addBook(formData);
}
}
//取消
cancelHandle(){
this.setState({
visible : false,
formData : {} //点击取消置空record对象
});
}
render(){
const { userList, deleteUser } = this.props; //connect传递的props
const { title,visible ,confirmLoading } = this.state;
console.log("===================userlist props:"+JSON.stringify(this.props));
const columns = [{
title : '用户编号',
dataIndex : 'id'
},{
title : '名称',
dataIndex : 'name'
},{
title:'学号',
dataIndex:'student_id'
},{
title:'性别',
dataIndex:'gender'
},{
title:'操作',
render : (text,record) => (
<span type="ghost">
<Button size="small" onClick={() => this.editHandle(record)}>编辑</Button>
<Divider type="vertical" />
<Popconfirm title="确定要删除吗?" onConfirm={() => deleteUser(record)}>
<Button size="small" >删除</Button>
</Popconfirm>
</span>
)
}];
return (
<div>
<div>
<SearchInput addHandle={this.addHandle.bind(this)}/>
</div>
<Table columns={columns} dataSource={userList}/>
<Modal
title={title}
visible= {visible}
confirmLoading = {confirmLoading}
onCancel = {() => this.cancelHandle()}
footer = {null}
>
<FormLayout record={this.state.formData} comfirmHandle={this.comfirmHandle.bind(this)}/>
</Modal>
</div>
);
}
}
UserList.contextTypes = {
store: PropTypes.object.isRequired
};
export default UserList;
//srcactionsuserActions.js
const INIT_USER_ACTION = "INIT_USER_ACTION";
const ADD_USER_ACTION = "ADD_USER_ACTION";
const DELETE_USER_ACTION = "DELETE_USER_ACTION";
const UPDATE_USER_ACTION = "UPDATE_USER_ACTION";
export const initUserAction = (data) => {
return {
type : INIT_USER_ACTION,
payload : data
}
}
export const addUserAction = (data) => {
return {
type : ADD_USER_ACTION,
payload : data
}
}
export const deleteUserAction = (id) => {
return {
type : DELETE_USER_ACTION,
payload : id
}
}
export const updateUserAction = (data) => {
return {
type : UPDATE_USER_ACTION,
payload : data
}
}
//srccomponentsSearchInput.js
import React from 'react';
import { Input ,Row ,Col ,Button } from 'antd';
const Search = Input.Search;
class SearchInput extends React.Component{
render(){
return (
//利用栅格系统,使searchInput和button在同一行
<div>
<Row>
<Col span={10}>
<Search
placeholder="输入编号查询..."
onSearch = {value => console.log(value)}
style={{ 400 ,marginBottom :20}}
enterButton = "搜索"
/>
</Col>
<Col span={3}>
<Button type="primary" onClick={this.props.addHandle}>添加</Button>
</Col>
</Row>
</div>
);
}
}
export default SearchInput;
//srccomponentsForm.js
import React from 'react';
import { Form , Input , Button } from 'antd';
const FormItem = Form.Item;
const formItemLayout = {
labelCol : {span : 5},
wrapperCol : {span : 15}
};
class FormLayout extends React.Component{
handleSubmit(e){
e.preventDefault();
const comfirmHandle = this.props.comfirmHandle;
const fieldsValue = this.props.form.getFieldsValue();
//表单校验
this.props.form.validateFields(function(errors,value){
//校验通过
if(!errors){
comfirmHandle(fieldsValue); //获取当前表单数据并当做回调函数的参数传递给父组件
}
});
}
render(){
const { getFieldDecorator ,getFeildsValue } = this.props.form;
const { record } = this.props;
return (
<Form onSubmit= {this.handleSubmit.bind(this)}>
<FormItem label="编号" {...formItemLayout} style={{display:'none'}}>
{getFieldDecorator('id', {
initialValue : record ? record.id : ""
})(
<Input />
)}
</FormItem>
<FormItem label="名称" {...formItemLayout}>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请输入书籍名称!'
}],
initialValue : record ? record.name : ""
})(
<Input placeholder="请输入书籍名称"/>
)}
</FormItem>
<FormItem label="价格" {...formItemLayout}>
{getFieldDecorator('price', {
rules: [{
required: true, message: '请输入价格!'
},{
pattern : /(^[1-9](d+)?(.d{1,2})?$)|(^(0){1}$)|(^d.d{1,2}?$)/,message:'请输入正确的金额'
}],
initialValue : record ? record.price : ""
})(
<Input placeholder="请输入价格" />
)}
</FormItem>
<FormItem label="借阅者编号" {...formItemLayout}>
{getFieldDecorator('owner_id', {
rules: [{
required: true, message: '请输入借阅者编号!'
},{
pattern : /^(d{5})$/,message:'请输入5位数字'
}],
initialValue : record ? record.owner_id :""
})(
<Input placeholder="请输入借阅者编号"/>
)}
</FormItem>
<FormItem wrapperCol={{ span: 10, offset: 10 }}>
<Button type="primary" htmlType="submit">
确定
</Button>
</FormItem>
</Form>
);
}
}
export default FormLayout = Form.create()(FormLayout);
//srccomponentsUserAdd.js
import React from 'react';
class UserAdd extends React.Component{
render(){
return (
<div>添加用户</div>
);
}
}
export default UserAdd;
//srccomponentsBookList.js
import React from 'react';
import { Table, Button, Popconfirm, Divider, Modal, message} from 'antd';
import {initBookAction} from "../actions/bookActions";
import PropTypes from 'prop-types';
import FormLayout from './Form';
import SearchInput from '../components/SearchInput';
class BookList extends React.Component{
constructor(props){
super(props);
this.state = {
title : "",
visible : false,
confirmLoading : false,
formData : {},
operation : ""
};
}
componentWillMount(){
const {store} = this.context;
fetch("http://localhost:3001/book")
.then(res => res.json())
.then(res => {
store.dispatch(initBookAction(res));
});
}
//点击编辑
editHandle(record){
//record:{"id":10002,"name":"PHP从入门到死亡","price":89,"owner_id":10002}
this.setState({
title : "修改",
visible : true,
formData : record,
operation : "edit" //编辑状态
});
}
//在子组件中点击添加需要调用的props函数
addHandle(){
this.setState({
title : "添加",
visible : true,
operation : "add"
});
}
//Form表单点击确定的时候要执行的props函数,动态获取Input组件的值
comfirmHandle(data){
//这个地方要注意setState是异步的,
//只有在重新render的时候state的值才会被重新修改
//所以通过回调函数解决
this.setState({
visible : false,
formData : data
},() => {
let { operation } = this.state;
const { formData } = this.state;
if(operation === "edit"){
this.props.editBook(formData);
}else{
this.props.addBook(formData);
//this.props.history.push("/book/list");
}
//处理完之后再次置空
this.setState({
formData : {}
})
})
}
//取消
cancelHandle(){
this.setState({
visible : false,
formData : {} //点击取消置空record对象
});
}
render(){
const { bookList, deleteBook } = this.props; //connect传递的props
const { title,visible ,confirmLoading } = this.state;
const columns = [{
title : '图书编号',
dataIndex : 'id',
key : 'id'
},{
title : '名称',
dataIndex : 'name',
key : 'name'
},{
title:'价格',
dataIndex:'price',
key : 'price'
},{
title:'借阅人编号',
dataIndex:'owner_id',
key : 'owner_id'
},{
title:'操作',
key : 'operation',
render : (text,record) => (
<span type="ghost">
<Button size="small" onClick={() => this.editHandle(record)}>编辑</Button>
<Divider type="vertical" />
<Popconfirm title="确定要删除吗?" onConfirm={() => deleteBook(record.id)}>
<Button size="small" >删除</Button>
</Popconfirm>
</span>
)
}];
return (
<div>
<div>
<SearchInput addHandle={this.addHandle.bind(this)}/>
</div>
<Table columns={columns} dataSource={bookList} rowKey="id"/>
<Modal
title={title}
visible= {visible}
confirmLoading = {confirmLoading}
onCancel = {this.cancelHandle.bind(this)}
footer = {null}
destroyOnClose
>
<FormLayout record={this.state.formData} comfirmHandle={this.comfirmHandle.bind(this)}/>
</Modal>
</div>
);
}
}
BookList.contextTypes = {
store: PropTypes.object.isRequired,
router:PropTypes.object.isRequired
};
export default BookList;
import React from 'react';
import Form from './Form';
class BookAdd extends React.Component{
render(){
return (
<div>
<Form/>
</div>
);
}
}
export default BookAdd;
//srccomponentsForm.js
import React from 'react';
import { Form , Input , Button } from 'antd';
const FormItem = Form.Item;
const formItemLayout = {
labelCol : {span : 5},
wrapperCol : {span : 15}
};
class FormLayout extends React.Component{
handleSubmit(e){
e.preventDefault();
const comfirmHandle = this.props.comfirmHandle;
const fieldsValue = this.props.form.getFieldsValue();
//表单校验
this.props.form.validateFields(function(errors,value){
//校验通过
if(!errors){
comfirmHandle(fieldsValue); //获取当前表单数据并当做回调函数的参数传递给父组件
}
});
}
render(){
const { getFieldDecorator ,getFeildsValue } = this.props.form;
const { record } = this.props;
return (
<Form onSubmit= {this.handleSubmit.bind(this)}>
<FormItem label="编号" {...formItemLayout} style={{display:'none'}}>
{getFieldDecorator('id', {
initialValue : record ? record.id : ""
})(
<Input />
)}
</FormItem>
<FormItem label="名称" {...formItemLayout}>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请输入书籍名称!'
}],
initialValue : record ? record.name : ""
})(
<Input placeholder="请输入书籍名称"/>
)}
</FormItem>
<FormItem label="价格" {...formItemLayout}>
{getFieldDecorator('price', {
rules: [{
required: true, message: '请输入价格!'
},{
pattern : /(^[1-9](d+)?(.d{1,2})?$)|(^(0){1}$)|(^d.d{1,2}?$)/,message:'请输入正确的金额'
}],
initialValue : record ? record.price : ""
})(
<Input placeholder="请输入价格" />
)}
</FormItem>
<FormItem label="借阅者编号" {...formItemLayout}>
{getFieldDecorator('owner_id', {
rules: [{
required: true, message: '请输入借阅者编号!'
},{
pattern : /^(d{5})$/,message:'请输入5位数字'
}],
initialValue : record ? record.owner_id :""
})(
<Input placeholder="请输入借阅者编号"/>
)}
</FormItem>
<FormItem wrapperCol={{ span: 10, offset: 10 }}>
<Button type="primary" htmlType="submit">
确定
</Button>
</FormItem>
</Form>
);
}
}
export default FormLayout = Form.create()(FormLayout);
//srccontainersBookList.js
import { connect } from 'react-redux';
import BookList from '../components/BookList';
import { deleteBookAction , addBookAction ,updateBookAction } from '../actions/bookActions';
import { message } from 'antd';
const mapStateToProps = (state) => {
return {
bookList : state.bookReducer.data
};
}
const mapDispatchToProps = (dispatch) => {
return {
deleteBook : (id) => {
//dispatch(deleteBookAction(id))
dispatch(dispatch => {
fetch('http://localhost:3001/book/'+id,{
method : 'delete'
})
.then(res => res.json())
.then(res => {
console.log("==============删除返回参数:"+JSON.stringify(res));
dispatch(deleteBookAction(id));
message.success("删除记录成功");
})
.catch(err => {
message.error("删除记录失败");
})
})
},
addBook : (data) => {
//dispatch(addBookAction(data))
dispatch(dispatch => {
fetch('http://localhost:3001/book',{
method : 'post',
body : JSON.stringify({
name : data.name,
price : data.price,
owner_id : data.owner_id
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json)
.then(res => {
console.log("==============添加返回参数:"+JSON.stringify(res));
dispatch(addBookAction(data))
//message.success("添加记录成功");
window.location.reload();
})
.catch(error => {
message.error("添加记录失败")
})
})
},
editBook : (data) => {
//dispatch(updateBookAction(data))
dispatch(dispatch => {
fetch('http://localhost:3001/book/'+data.id,{
method : 'put',
body : JSON.stringify({
name : data.name,
price : data.price,
owner_id : data.owner_id
}),
headers : {
'Content-Type' : 'application/json'
}
})
.then(res => res.json())
.then(res => {
console.log("==============修改返回参数:"+JSON.stringify(res));
dispatch(updateBookAction(data))
message.success("修改记录成功")
})
.catch(error => {
message.error("修改记录失败")
})
})
}
}
}
const BookListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(BookList);
export default BookListContainer;
//srccontainersUserList.js
import { connect } from 'react-redux';
import UserList from '../components/UserList';
import { deleteUserAction ,addUserAction ,updateUserAction } from '../actions/userActions';
const mapStateToProps = (state) => {
return {
userList : state.userReducer.data
};
}
const mapDispatchToProps = (dispatch) => {
return {
deleteUser : (id) => {
dispatch(deleteUserAction(id))
},
addUser : (data) => {
dispatch(addUserAction(data))
},
editUser : (data) => {
dispatch(updateUserAction(data))
}
}
}
const UserListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(UserList);
export default UserListContainer;