react+react-router+redux開發體育館管理系統(3)--場地型別管理模組開發
接下來實現的是場地型別管理模組,先把主頁的框架寫出來,ui庫使用的是antd
附官網地址:https://ant.design/index-cn
主頁框架實現
在container目錄下新建index.js(原來那個Main.js不要了)
index.js
require('normalize.css/normalize.css');
require('styles/App.css');
import React from 'react';
//路由模組
import {Link} from 'react-router-dom'
//引入antd模組
import {Layout, Menu, Icon} from 'antd'
const SubMenu = Menu.SubMenu;
const {Header, Content, Sider} = Layout;
import 'antd/dist/antd.css'
let yeomanImage = require('../images/yeoman.png');
class AppComponent extends React.Component {
state = {
current: '',
username: 'shilim',
collapsed: false,
mode:'inline'
}
toggle = () => {
this.setState({
collapsed: !this.state.collapsed,
mode: this.state.collapsed ? 'inline' : 'vertical'
})
}
handleClick = (e) => {
this.setState({
current: e.key
});
}
render() {
return (
<Layout id="main-view">
<Sider
trigger={null}
collapsible
collapsed={this.state.collapsed}
>
<img src={yeomanImage} width="50" id="logo"/>
<Menu theme="dark"
onClick={this.handleClick}
defaultSelectedKeys ={[this.state.current]}
mode={this.state.mode}
>
<SubMenu key="sub1" title={<span><Icon type="user"/><span>使用者管理</span></span>}>
<Menu.Item key="1"><Link to="/userList">使用者管理</Link></Menu.Item>
<Menu.Item key="2"><Link to="/roleList">角色管理</Link></Menu.Item>
</SubMenu>
<SubMenu key="sub2" title={<span><Icon type="tool"/><span>器材管理</span></span>}>
<Menu.Item key="4"><Link to="/equipmentTypeList">型別管理</Link></Menu.Item>
<Menu.Item key="5"><Link to="/equipmentList">器材管理</Link></Menu.Item>
<Menu.Item key="6"><Link to="/equipmentLoanList">租借管理</Link></Menu.Item>
</SubMenu>
<SubMenu key="sub3" title={<span><Icon type="appstore-o"/><span>場地管理</span></span>}>
<Menu.Item key="7"><Link to="/placeTypeList">型別管理</Link></Menu.Item>
<Menu.Item key="8"><Link to="/placeList">場地管理</Link></Menu.Item>
<Menu.Item key="9"><Link to="/placeLeaseRecordList">租借管理</Link></Menu.Item>
</SubMenu>
<SubMenu key="sub4" title={<span><Icon type="schedule"/><span>賽事管理</span></span>}>
<Menu.Item key="10"><Link to="/gameList">賽事管理</Link></Menu.Item>
<Menu.Item key="11"><Link to="/noticeList">公告管理</Link></Menu.Item>
</SubMenu>
</Menu>
</Sider>
<Layout>
<Header style={{background: '#fff', padding: 0,borderBottom:'1px solid #e9e9e9'}}>
<Icon
className="trigger"
type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'}
onClick={this.toggle}
/>
<span>體育館管理系統</span>
</Header>
<Content>
{ this.props.children }
</Content>
</Layout>
</Layout>
);
}
}
AppComponent.defaultProps = {};
export default AppComponent;
App.css
/* Base Application Styles */
html,body,#app{
width: 100%;
height: 100%;
}
#main-view{
height: 100%;
}
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 16px;
cursor: pointer;
transition: color .3s;
}
.trigger:hover {
color: #108ee9;
}
#logo {
display: block;
margin: 20px auto;
}
.ant-layout-sider-collapsed .anticon {
font-size: 16px;
}
.ant-layout-sider-collapsed .nav-text {
display: none;
}
路由配置
如果沒有安裝react-router通過npm install react-router -save
安裝
修改routers.js
import React from 'react';
import {HashRouter as Router, Route} from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
const history = createBrowserHistory()
import App from '../containers/index';
export default class RouterMap extends React.Component {
render() {
return (
<Router history={history}>
<App>
</App>
</Router>)
}
}
啟動來看一下,選單已經出來了:
場地型別列表頁實現
場地列表頁使用了redux,其實這個專案並不需要用到redux。因為redux的狀態應該是多個元件共享的狀態,主要是為了解決元件間通訊的困難。這個專案中其實每個元件自己維護自己的狀態就已經足夠了,這裡使用redux只是為了練習其用法。
1. reducer
先完成reducer
placeTypeManage.js
import * as actionTypes from '../constants/placeTypeManage';
import PageVo from '../models/PageVo';
const initialState = {
page:new PageVo(1,5)
}
export function placeTypeManage(state=initialState,action) {
switch (action.type) {
case actionTypes.CHANGE_PAGE_NUM:
return {
...state,
page:action.page
}
case actionTypes.SAVE_PLACE_TYPE_LIST:
return {
...state,
placeTypeList:action.data
}
default:
return state;
}
}
修改reducers目錄下的index.js,用於整合不同的reducer,便於分模組
import { combineReducers } from 'redux'
import { placeTypeManage } from './placeTypeManage'
export default combineReducers({
placeTypeManage
})
2. action
view想要改變state,只能通過出發action,action通過dispatch來通過reducer改變state。修改action下面的
placeTypeManage.js
import * as actionTypes from '../constants/placeTypeManage';
import * as api from '../api/placeTypeManageApi';
const qs = require('qs');
import {notificationShow} from '../utils/notification'
export function changePageNum(page) {
return {
type: actionTypes.CHANGE_PAGE_NUM,
page: page
}
}
export function getPlaceTypeList(page) {
const pageVo = qs.stringify(page);
return dispatch => api.getPlaceTypeList(pageVo)
.then((res) => {
switch (res.data.serviceResult) {
case 1:
if(res.data.resultParam.list.length===0 && res.data.resultParam.pageNum>0) {
let tempPage = JSON.parse(page.page);
tempPage.pageNum--;
dispatch(getPlaceTypeList({page:JSON.stringify(tempPage)}));
} else {
dispatch(savePlaceTypeList(res.data.resultParam))
}
}
})
.catch((error) => {
notificationShow('error',error)
})
}
export function savePlaceTypeList(data) {
return {
type: actionTypes.SAVE_PLACE_TYPE_LIST,
data: data
}
}
這裡使用了qs,主要用於格式化傳送的引數,axios使用post請求時,引數會被加上一個雙引號,後臺拿到的資料都會被加上雙引號。所以使用qs使其變正常,不然後臺無法接收到正常的引數。qs可以通過npm install qs --save
下載
3. store
主要用於初始化state,修改stores下面的index.js
import {createStore,applyMiddleware} from 'redux';
import reducers from '../reducers/index';
import thunk from 'redux-thunk';
export default function configureStore() {
let store = createStore(reducers,applyMiddleware(thunk),
// 觸發 redux-devtools
window.devToolsExtension ? window.devToolsExtension() : undefined
);
return store;
}
這裡使用了一箇中間件thunk,主要是action能夠進行非同步操作,具體解釋可以自行查資料
4. 頁面實現
修改containers目錄下的placeType目錄下的placeTypeList.js
/**
* Created by shilim on 2017/7/11.
*/
import React from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {Breadcrumb, Button, Table, Icon, Pagination,Modal,Input} from 'antd'
const confirm = Modal.confirm;
const Search = Input.Search;
import {changePageNum, getPlaceTypeList} from '../../actions/placeTypeManage'
import {deletePlaceType} from '../../api/placeTypeManageApi'
import {notificationShow} from '../../utils/notification'
import PageVo from '../../models/PageVo'
import PlaceTypeVo from '../../models/PlaceTypeVo'
import QueueAnim from 'rc-queue-anim'
const qs = require('qs')
class PlaceTypePage extends React.Component {
state = {
selectedRowKeys: []
}
onSelectChange = (selectedRowKeys) => {
this.setState({selectedRowKeys});
}
onSearch = (value) => {
this.props.changePageNum(new PageVo(this.props.page.pageNum,
this.props.page.pageSize,null,value,true))
setTimeout(()=> {
this.props.getPlaceTypeList({page: this.props.page.voToJson()})
})
}
onPageChange = (pageNum, pageSize) => {
this.props.changePageNum(new PageVo(pageNum, pageSize))
setTimeout(()=> {
this.props.getPlaceTypeList({page: this.props.page.voToJson()})
})
}
onDeleteOne = (id) => {
confirm({
title:'您確認刪除該記錄嗎?',
onOk:() => {
let placeTypeVo = new PlaceTypeVo();
placeTypeVo.id = id;
let placeTypeList = [];
placeTypeList.push(placeTypeVo);
deletePlaceType(qs.stringify({placeTypeList:JSON.stringify(placeTypeList)}))
.then((res) => {
switch (res.data.serviceResult) {
case 1:
notificationShow('success',res.data.resultInfo)
this.props.getPlaceTypeList({page: this.props.page.voToJson()})
break;
default:
notificationShow('error', res.data.resultInfo)
break;
}
})
.catch((err) => {
notificationShow('error',err)
})
}
})
}
render() {
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: this.onSelectChange
}
const columns = [{
title: '場地名稱',
dataIndex: 'placeTypeName'
}, {
title: '操作',
width:150,
render: (item) => (
<span>
<Link to={'/editPlaceType/'+item.id}><Icon type="edit"/></Link>
<Icon type="delete" style={{cursor: 'pointer', marginLeft: 20,color:'red'}}
onClick={() => this.onDeleteOne(item.id)}/>
</span>
)
}];
return (
<QueueAnim duration="1200">
<div key="placeTypeList" style={{padding: 15, background: '#fff', minHeight: 360}}>
<Breadcrumb>
<Breadcrumb.Item>場地管理</Breadcrumb.Item>
<Breadcrumb.Item>場地型別列表</Breadcrumb.Item>
</Breadcrumb>
<div style={{margin: '15px 0'}}>
<Link to="/addPlaceType"><Button type="primary" icon="plus">新增場地型別</Button></Link>
<Search
placeholder="輸入型別名稱查詢"
style={{ width: 200 ,float:'right'}}
onSearch={this.onSearch}
/>
</div>
<div>
<Table rowKey="id" rowSelection={rowSelection} columns={columns} pagination={false}
dataSource={this.props.placeTypeList ? this.props.placeTypeList.list : null}/>
{
this.props.placeTypeList ? (<Pagination style={{marginTop: 15}}
showTotal={(total) => `共${total}條資料`}
total={this.props.placeTypeList.total}
pageSize={this.props.placeTypeList.pageSize}
defaultCurrent={this.props.placeTypeList.pageNum}
current={this.props.placeTypeList.pageNum}
showQuickJumper={true}
onChange={this.onPageChange}></Pagination>) : (<div>載入中...</div>)
}
</div>
</div>
</QueueAnim>)
}
componentDidMount() {
this.props.getPlaceTypeList({page: this.props.page.voToJson()})
}
}
function mapStateToProps(state) {
return {
page: state.placeTypeManage.page,
placeTypeList: state.placeTypeManage.placeTypeList
}
}
function mapDispatchToProps(dispatch) {
return {
changePageNum: (page) => {
dispatch(changePageNum(page))
},
getPlaceTypeList: (page) => {
dispatch(getPlaceTypeList(page))
}
}
}
PlaceTypePage = connect(
mapStateToProps,
mapDispatchToProps
)(PlaceTypePage)
export default PlaceTypePage
connect就是把react和redux連線起來的關鍵,這個函式允許我們將 store 中的資料和action裡的方法作為 props 繫結到元件上。這樣我就可以展示store裡面的資料,也能夠通過呼叫action來修改state。
場地型別新增頁實現
場地型別增加就不使用redux了,因為狀態自己管理就已經足夠了
修改containers目錄下的placeType目錄下的placeTypeAddition.js
import React from 'react';
import QueueAnim from 'rc-queue-anim'
import {Breadcrumb, Button, Popconfirm, Form, Input} from 'antd';
const FormItem = Form.Item;
import PlaceTypeVo from '../../models/PlaceTypeVo';
import {addPlaceType} from '../../api/placeTypeManageApi';
import {notificationShow} from '../../utils/notification';
const qs = require('qs');
class PlaceTypeAddictionPage extends React.Component {
goBack = () => {
this.props.history.push('/placeTypeList')
}
handleSubmit = (e) => {
e.preventDefault();
let placeTypeVo = new PlaceTypeVo();
this.props.form.validateFields((err, values) => {
if (!err) {
placeTypeVo = Object.assign(placeTypeVo, values)
addPlaceType(qs.stringify({placeType: placeTypeVo.voToJson()}))
.then((res) => {
switch (res.data.serviceResult) {
case 1:
notificationShow('success', res.data.resultInfo);
this.goBack();
break;
default:
notificationShow('error', res.data.resultInfo);
break;
}
})
.catch((error) => {
notificationShow('error',error)
})
}
});
}
render() {
const {getFieldDecorator} = this.props.form;
const formItemLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 6}
},
wrapperCol: {
xs: {span: 24},
sm: {span: 14}
}
};
return (
<QueueAnim duration="1200">
<div key="placeTypeAddiction" style={{padding: 15, background: '#fff'}}>
<Breadcrumb>
<Breadcrumb.Item>場地管理</Breadcrumb.Item>
<Breadcrumb.Item>新增場地型別</Breadcrumb.Item>
</Breadcrumb>
<div style={{margin: '15px 0'}}>
<Popconfirm placement="top" title="您確定放棄當前新增記錄嗎?" okText="是" cancelText="否"
onConfirm={this.goBack}>
<Button type="default" icon="rollback">返回</Button>
</Popconfirm>
<Button type="primary" icon="file" style={{marginLeft: 10}} onClick={this.handleSubmit}>儲存</Button>
</div>
<div style={{borderTop: '1px dashed #e7eaec', margin: '20px 0'}}/>
<Form>
<FormItem {...formItemLayout} label="型別名稱" hasFeedback>
{getFieldDecorator('placeTypeName', {rules: [{required: true, message: '型別名稱不能為空'}]})(
<Input/>
)}
</FormItem>
</Form>
</div>
</QueueAnim>
)
}
componentDidMount() {
}
}
PlaceTypeAddictionPage = Form.create()(PlaceTypeAddictionPage);
export default PlaceTypeAddictionPage
場地型別修改頁實現
修改containers目錄下的placeType目錄下的placeTypeEdition.js
import React from 'react';
import QueueAnim from 'rc-queue-anim'
import {Breadcrumb, Button, Popconfirm, Form, Input} from 'antd';
const FormItem = Form.Item;
import PlaceTypeVo from '../../models/PlaceTypeVo';
import {getOnePlaceType,updatePlaceType} from '../../api/placeTypeManageApi';
import {notificationShow} from '../../utils/notification';
const qs = require('qs');
class PlaceTypeEditionPage extends React.Component {
constructor(props) {
super(props)
this.state = {placeType:{}}
}
goBack = () => {
this.props.history.push('/placeTypeList')
}
handleSubmit = (e) => {
e.preventDefault();
let placeTypeVo = new PlaceTypeVo();
this.props.form.validateFields((err, values) => {
if (!err) {
placeTypeVo = Object.assign(placeTypeVo,this.state.placeType,values)
updatePlaceType(qs.stringify({placeType: placeTypeVo.voToJson()}))
.then((res) => {
switch (res.data.serviceResult) {
case 1:
notificationShow('success', res.data.resultInfo);
this.goBack();
break;
default:
notificationShow('error', res.data.resultInfo);
break;
}
})
.catch((error) => {
notificationShow('error',error)
})
}
});
}
render() {
const {getFieldDecorator} = this.props.form;
const formItemLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 6}
},
wrapperCol: {
xs: {span: 24},
sm: {span: 14}
}
};
return (
<QueueAnim duration="1200">
<div key="placeTypeAddiction" style={{padding: 15, background: '#fff'}}>
<Breadcrumb>
<Breadcrumb.Item>場地管理</Breadcrumb.Item>
<Breadcrumb.Item>修改場地型別</Breadcrumb.Item>
</Breadcrumb>
<div style={{margin: '15px 0'}}>
<Popconfirm placement="top" title="您確定放棄當前新增記錄嗎?" okText="是" cancelText="否"
onConfirm={this.goBack}>
<Button type="default" icon="rollback">返回</Button>
</Popconfirm>
<Button type="primary" icon="file" style={{marginLeft: 10}} onClick={this.handleSubmit}>儲存</Button>
</div>
<div style={{borderTop: '1px dashed #e7eaec', margin: '20px 0'}}/>
<Form>
<FormItem {...formItemLayout} label="型別名稱" hasFeedback>
{getFieldDecorator('placeTypeName', {rules: [{required: true, message: '型別名稱不能為空'}]})(
<Input/>
)}
</FormItem>
</Form>
</div>
</QueueAnim>
)
}
componentDidMount() {
let placeTypeVo = new PlaceTypeVo(this.props.match.params.id);
getOnePlaceType(qs.stringify({placeType:placeTypeVo.voToJson()}))
.then(res => {
this.setState({...this.state,placeType:res.data.resultParam})
this.props.form.setFieldsValue(this.state.placeType)
})
.catch(err => {
notificationShow('error',err)
})
}
}
PlaceTypeEditionPage = Form.create()(PlaceTypeEditionPage);
export default PlaceTypeEditionPage
上面所使用的工具類
untils/notification.js
import {notification} from 'antd';
export function notificationShow(type,description) {
notification[type]({
message:'提示',
description:description,
duration:2
})
}
新增路由
頁面寫完了,還要為三個頁面配置路由
import App from '../containers/index';
import PlaceTypeListPage from '../containers/PlaceType/PlaceTypeList'
import PlaceTypeAddictionPage from '../containers/PlaceType/PlaceTypeAddiction'
import PlaceTypeEditionPage from '../containers/PlaceType/PlaceTypeEdition'
export default class RouterMap extends React.Component {
render() {
return (
<Router history={history}>
<App>
<Route path="/placeTypeList" component={PlaceTypeListPage}/>
<Route path="/addPlaceType" component={PlaceTypeAddictionPage}/>
<Route path="/editPlaceType/:id" component={PlaceTypeEditionPage}/>
</App>
</Router>)
}
}
到此場地型別的所有功能就已經完成了,可以啟動服務檢視。
總結
這裡只是對redux進行了簡單的使用,瞭解大致的流程,目錄結構的搭建。要想進一步學習,還得繼續參考別人的專案,深入學習。結合別人的專案和文件可以更好的學習,有些時候文件可能有些東西你沒看到,剛好可以從別人的專案的裡面找到。或者文件裡面沒有說明的東西也可以從別人的專案裡面學到,很多程式碼都要自己嘗試出來,作為以後的參考。
小提示:及時把程式碼上傳的Git是個好習慣