React全家桶+AntD 单车后台管理系统开发(完整版)
(第8章 单车业务之城市管理模块&)
第8章 单车业务之城市管理模块&
src/router.js,通用路由
import React from 'react'
import { HashRouter, Route, Switch, Redirect} from 'react-router-dom'
import App from './App'
import Login from './pages/login'
import Admin from './admin'
import Home from './pages/home';
import Buttons from './pages/ui/buttons'
import Modals from './pages/ui/modals'
import NoMatch from './pages/nomatch'
import Loadings from './pages/ui/loadings'
import Notice from './pages/ui/notice'
import Messages from './pages/ui/messages'
import Tabs from './pages/ui/tabs'
import Gallery from './pages/ui/gallery'
import Carousel from './pages/ui/carousel'
import FormLogin from './pages/form/login'
import FormRegister from './pages/form/register'
import BasicTable from './pages/table/basicTable'
import HighTable from './pages/table/highTable'
import Rich from './pages/rich'
import City from './pages/city/index'
import Order from './pages/order/index'
import Common from './common'
import OrderDetail from './pages/order/detail'
import BikeMap from './pages/map/bikeMap'
import User from './pages/user/index'
import Bar from './pages/echarts/bar/index'
import Pie from './pages/echarts/pie/index'
import Line from './pages/echarts/line/index'
import Permission from './pages/permission'
export default class ERouter extends React.Component{
render(){
return (
}
/>
{/* */}
} />
);
}
}
src\pages\city\index.js
axios动态获取数据,table数组数据字典,card布局,FormItem的栅格布局,Modal弹出框
import React from 'react';
import { Card, Button, Table, Form, Select, Modal, message } from 'antd';
import axios from './../../axios/index';
import Utils from './../../utils/utils';
const FormItem = Form.Item;
const Option = Select.Option;
export default class City extends React.Component{
state = {
list:[],
isShowOpenCity:false
}
params = {
page:1
}
componentDidMount(){
this.requestList();
}
// 默认请求我们的接口数据
requestList = ()=>{
let _this = this;
axios.ajax({
url: '/open_city',
data:{
params:{
page:this.params.page
}
}
}).then((res)=>{
let list = res.result.item_list.map((item, index) => {
item.key = index;
return item;
});
this.setState({
list:list,
pagination:Utils.pagination(res,(current)=>{
_this.params.page = current;
_this.requestList();
})
})
})
}
// 开通城市
handleOpenCity = ()=>{
this.setState({
isShowOpenCity:true
})
}
// 城市开通提交
handleSubmit = ()=>{
let cityInfo = this.cityForm.props.form.getFieldsValue();
console.log(cityInfo);
axios.ajax({
url:'/city/open',
data:{
params:cityInfo
}
}).then((res)=>{
if(res.code == '0'){
message.success('开通成功');
this.setState({
isShowOpenCity:false
})
this.requestList();
}
})
}
render(){
const columns = [
{
title:'城市ID',
dataIndex:'id'
}, {
title: '城市名称',
dataIndex: 'name'
}, {
title: '用车模式',
dataIndex: 'mode',
render(mode){
return mode ==1 ?'停车点':'禁停区';
}
}, {
title: '营运模式',
dataIndex: 'op_mode',
render(op_mode) {
return op_mode == 1 ? '自营' : '加盟';
}
}, {
title: '授权加盟商',
dataIndex: 'franchisee_name'
}, {
title: '城市管理员',
dataIndex: 'city_admins',
render(arr){
return arr.map((item)=>{
return item.user_name;
}).join(',');
}
}, {
title: '城市开通时间',
dataIndex: 'open_time'
}, {
title: '操作时间',
dataIndex: 'update_time',
render: Utils.formateDate
}, {
title: '操作人',
dataIndex: 'sys_user_name'
}
]
return (
{marginTop:10}}
开通城市
{
this.setState({
isShowOpenCity:false
})
}}
onOk={this.handleSubmit}
>
{this.cityForm = inst;}}/>
);
}
}
class FilterForm extends React.Component{
render(){
const { getFieldDecorator } = this.props.form;
return (
{
getFieldDecorator('city_id')(
{width:100}}
placeholder="全部"
全部
北京市
天津市
深圳市
)
}
{
getFieldDecorator('mode')(
{ width: 120 }}
placeholder="全部"
全部
指定停车点模式
禁停区模式
)
}
{
getFieldDecorator('op_mode')(
{ width: 80 }}
placeholder="全部"
全部
自营
加盟
)
}
{
getFieldDecorator('auth_status')(
{ width: 100 }}
placeholder="全部"
全部
已授权
未授权
)
}
{margin:'0 20px'}}查询
重置
);
}
}
FilterForm = Form.create({})(FilterForm);
class OpenCityForm extends React.Component{
render(){
const formItemLayout = {
labelCol:{
span:5
},
wrapperCol:{
span:19
}
}
const { getFieldDecorator } =this.props.form;
return (
{
getFieldDecorator('city_id',{
initialValue:'1'
})(
{ width: 100 }}
全部
北京市
天津市
)
}
{
getFieldDecorator('op_mode', {
initialValue: '1'
})(
{ width: 100 }}
自营
加盟
)
}
{
getFieldDecorator('use_mode', {
initialValue: '1'
})(
{ width: 100 }}
指定停车点
禁停区
)
}
);
}
}
OpenCityForm = Form.create({})(OpenCityForm);
common.less
@import './default.less';
@import './loading.less';
ul,li{
list-style: none;
}
.clearfix{
&::after{
content:' ';
clear:both;
display: block;
visibility: hidden;
}
}
.container{
.nav-left{
background-color:#001529;
color: #ffffff;
height: calc(100vh);
}
.main{
height: calc(100vh);
background-color: @colorL;
overflow: auto;
}
.content{
position: relative;
padding: 20px;
}
}
.content-wrap{
background: #ffffff;
border: 1px solid #e8e8e8;
margin-top: -3px;
.ant-table-wrapper{
margin-left: -1px;
margin-right: -2px;
}
}
.ant-card{
button{
margin-right: 10px;
}
}
utils.js
格式化日期
import React from 'react';
import { Select } from 'antd'
const Option = Select.Option;
export default {
formateDate(time){
if(!time)return '';
let date = new Date(time);
return date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+' '+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds();
},
pagination(data,callback){
return {
onChange:(current)=>{
callback(current)
},
current:data.result.page,
pageSize:data.result.page_size,
total: data.result.total_count,
showTotal:()=>{
return `共${data.result.total_count}条`
},
showQuickJumper:true
}
},
// 格式化金额,单位:分(eg:430分=4.30元)
formatFee(fee, suffix = '') {
if (!fee) {
return 0;
}
return Number(fee).toFixed(2) + suffix;
},
// 格式化公里(eg:3000 = 3公里)
formatMileage(mileage, text) {
if (!mileage) {
return 0;
}
if (mileage >= 1000) {
text = text || " km";
return Math.floor(mileage / 100) / 10 + text;
} else {
text = text || " m";
return mileage + text;
}
},
// 隐藏手机号中间4位
formatPhone(phone) {
phone += '';
return phone.replace(/(\d{3})\d*(\d{4})/g, '$1***$2')
},
// 隐藏身份证号中11位
formatIdentity(number) {
number += '';
return number.replace(/(\d{3})\d*(\d{4})/g, '$1***********$2')
},
getOptionList(data){
if(!data){
return [];
}
let options = [] //[全部];
data.map((item)=>{
options.push({item.name})
})
return options;
},
/**
* ETable 行点击通用函数
* @param {*选中行的索引} selectedRowKeys
* @param {*选中行对象} selectedItem
*/
updateSelectedItem(selectedRowKeys, selectedRows, selectedIds) {
if (selectedIds) {
this.setState({
selectedRowKeys,
selectedIds: selectedIds,
selectedItem: selectedRows
})
} else {
this.setState({
selectedRowKeys,
selectedItem: selectedRows
})
}
},
}
(第9章 单车业务之订单管理模块&)
第9章 单车业务之订单管理模块&
src\pages\order\index.js
打开一个新窗口window.open
import React from 'react';
import { Card, Button, Table, Form, Select, Modal, DatePicker, message} from 'antd'
import axios from '../../axios'
import Utils from '../../utils/utils'
import BaseForm from '../../components/BaseForm'
const FormItem = Form.Item;
const Option = Select.Option;
export default class Order extends React.Component{
state = {
orderInfo:{},
orderConfirmVisble:false
}
params = {
page: 1
}
formList = [
{
type:'SELECT',
label:'城市',
field:'city',
placeholder:'全部',
initialValue:'1',
width:80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '北京' }, { id: '2', name: '天津' }, { id: '3', name: '上海' }]
},
{
type: '时间查询'
},
{
type: 'SELECT',
label: '订单状态',
field:'order_status',
placeholder: '全部',
initialValue: '1',
width: 80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '进行中' }, { id: '2', name: '结束行程' }]
}
]
componentDidMount(){
this.requestList()
}
handleFilter = (params)=>{
this.params = params;
this.requestList();
}
requestList = ()=>{
let _this = this;
axios.ajax({
url:'/order/list',
data:{
params: this.params
}
}).then((res)=>{
let list = res.result.item_list.map((item, index) => {
item.key = index;
return item;
});
this.setState({
list,
pagination: Utils.pagination(res, (current) => {
_this.params.page = current;
_this.requestList();
})
})
})
}
// 订单结束确认
handleConfirm = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请选择一条订单进行结束'
})
return;
}
axios.ajax({
url:'/order/ebike_info',
data:{
params:{
orderId: item.id
}
}
}).then((res)=>{
if(res.code ==0 ){
this.setState({
orderInfo:res.result,
orderConfirmVisble: true
})
}
})
}
// 结束订单
handleFinishOrder = ()=>{
let item = this.state.selectedItem;
axios.ajax({
url: '/order/finish_order',
data: {
params: {
orderId: item.id
}
}
}).then((res) => {
if (res.code == 0) {
message.success('订单结束成功')
this.setState({
orderConfirmVisble: false
})
this.requestList();
}
})
}
onRowClick = (record, index) => {
let selectKey = [index];
this.setState({
selectedRowKeys: selectKey,
selectedItem: record
})
}
openOrderDetail = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请先选择一条订单'
})
return;
}
window.open(`/#/common/order/detail/${item.id}`,'_blank')
}
render(){
const columns = [
{
title:'订单编号',
dataIndex:'order_sn'
},
{
title: '车辆编号',
dataIndex: 'bike_sn'
},
{
title: '用户名',
dataIndex: 'user_name'
},
{
title: '手机号',
dataIndex: 'mobile'
},
{
title: '里程',
dataIndex: 'distance',
render(distance){
return distance/1000 + 'Km';
}
},
{
title: '行驶时长',
dataIndex: 'total_time'
},
{
title: '状态',
dataIndex: 'status'
},
{
title: '开始时间',
dataIndex: 'start_time'
},
{
title: '结束时间',
dataIndex: 'end_time'
},
{
title: '订单金额',
dataIndex: 'total_fee'
},
{
title: '实付金额',
dataIndex: 'user_pay'
}
]
const formItemLayout = {
labelCol:{span:5},
wrapperCol:{span:19}
}
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
type: 'radio',
selectedRowKeys
}
return (
{marginTop:10}}
订单详情
{marginLeft:10}} onClick={this.handleConfirm}结束订单
{
return {
onClick: () => {
this.onRowClick(record, index);
}
};
}}
/>
{
this.setState({
orderConfirmVisble:false
})
}}
onOk={this.handleFinishOrder}
width={600}
>
{this.state.orderInfo.bike_sn}
{this.state.orderInfo.battery + '%'}
{this.state.orderInfo.start_time}
{this.state.orderInfo.location}
);
}
}
通用页面设计,入口文件
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
{this.props.children}
);
}
}
export default App;
App.css
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
admin.js
import React from 'react'
import { Row,Col } from 'antd';
import Header from './components/Header'
import Footer from './components/Footer'
import { connect } from 'react-redux'
import NavLeft from './components/NavLeft'
import './style/common.less'
import Home from './pages/home'
class Admin extends React.Component{
render(){
return (
{/* */}
{this.props.children}
);
}
}
export default connect()(Admin)
common.js
import React from 'react'
import { Row, Col } from 'antd';
import Header from './components/Header'
import './style/common.less'
export default class Common extends React.Component {
render() {
return (
{this.props.children}
);
}
}
src\components\Header\index.js
面包屑,Row,Col
import React from 'react'
import { Row,Col } from "antd"
import './index.less'
import Util from '../../utils/utils'
import axios from '../../axios'
import { connect } from 'react-redux'
class Header extends React.Component{
state={}
componentWillMount(){
this.setState({
userName:'河畔一角'
})
setInterval(()=>{
let sysTime = Util.formateDate(new Date().getTime());
this.setState({
sysTime
})
},1000)
this.getWeatherAPIData();
}
getWeatherAPIData(){
let city = '北京';
axios.jsonp({
url:'http://api.map.baidu.com/telematics/v3/weather?location='+encodeURIComponent(city)+'&output=json&ak=3p49MVra6urFRGOT9s8UBWr2'
}).then((res)=>{
if(res.status == 'success'){
let data = res.results[0].weather_data[0];
this.setState({
dayPictureUrl:data.dayPictureUrl,
weather:data.weather
})
}
})
}
render(){
const { menuName, menuType } = this.props;
return (
{
menuType?
IMooc 通用管理系统
:''
}
欢迎,{this.state.userName}
退出
{
menuType?'':
{menuName || '首页'}
{this.state.sysTime}
{this.state.weather}
}
);
}
}
const mapStateToProps = state => {
return {
menuName: state.menuName
}
};
export default connect(mapStateToProps)(Header)
src\components\Header\index.less
@import './../../style/default.less';
.header{
background-color: @colorM;
.header-top{
height: 60px;
line-height: 60px;
padding: 0 20px;
text-align: right;
.logo{
line-height: 60px;
text-align: left;
font-size: 18px;
img{
height: 40px;
vertical-align: middle;
}
span{
margin-left: 5px;
}
}
a{
margin-left: 40px;
}
}
.breadcrumb{
height: 40px;
line-height: 40px;
padding: 0 20px;
border-top: 1px solid #f9c700;
.breadcrumb-title{
text-align: center;
font-size: @fontC;
&:after{
position: absolute;
content: '';
left:73px;
top:39px;
border-top: 9px solid @colorM;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
}
}
.weather{
text-align: right;
font-size: 14px;
.date{
margin-right: 10px;
vertical-align: middle;
}
.weather-img{
img{
height: 15px;
}
}
.weather-detail{
margin-left: 5px;
vertical-align: middle;
}
}
}
}
.simple-page{
.header-top{
background:#1890ff;
color:@colorM;
}
}
detail.js
接收动态变量,初始化地图组件,绘制地图
import React from 'react';
import { Card } from 'antd'
import axios from '../../axios'
import './detail.less'
export default class Order extends React.Component {
state = {}
componentDidMount(){
let orderId = this.props.match.params.orderId;
if(orderId){
this.getDetailInfo(orderId);
}
}
getDetailInfo = (orderId)=>{
axios.ajax({
url:'/order/detail',
data:{
params:{
orderId: orderId
}
}
}).then((res)=>{
if(res.code ==0){
this.setState({
orderInfo:res.result
})
this.renderMap(res.result);
}
})
}
renderMap = (result)=>{
this.map = new window.BMap.Map('orderDetailMap');
// this.map.centerAndZoom('北京',11);
// 添加地图控件
this.addMapControl();
// 调用路线图绘制方法
this.drawBikeRoute(result.position_list);
// 调用服务区绘制方法
this.drwaServiceArea(result.area);
}
// 添加地图控件
addMapControl = ()=>{
let map = this.map;
map.addControl(new window.BMap.ScaleControl({ anchor: window.BMAP_ANCHOR_TOP_RIGHT}));
map.addControl(new window.BMap.NavigationControl({ anchor: window.BMAP_ANCHOR_TOP_RIGHT }));
}
// 绘制用户的行驶路线
drawBikeRoute = (positionList)=>{
let map = this.map;
let startPoint = '';
let endPoint = '';
if (positionList.length>0){
let first = positionList[0];
let last = positionList[positionList.length-1];
startPoint = new window.BMap.Point(first.lon,first.lat);
let startIcon = new window.BMap.Icon('/assets/start_point.png',new window.BMap.Size(36,42),{
imageSize:new window.BMap.Size(36,42),
anchor: new window.BMap.Size(18, 42)
})
let startMarker = new window.BMap.Marker(startPoint, { icon: startIcon});
this.map.addOverlay(startMarker);
endPoint = new window.BMap.Point(last.lon, last.lat);
let endIcon = new window.BMap.Icon('/assets/end_point.png', new window.BMap.Size(36, 42), {
imageSize: new window.BMap.Size(36, 42),
anchor: new window.BMap.Size(18, 42)
})
let endMarker = new window.BMap.Marker(endPoint, { icon: endIcon });
this.map.addOverlay(endMarker);
// 连接路线图
let trackPoint = [];
for(let i=0;i{
// 连接路线图
let trackPoint = [];
for (let i = 0; i < positionList.length; i++) {
let point = positionList[i];
trackPoint.push(new window.BMap.Point(point.lon, point.lat));
}
// 绘制服务区
let polygon = new window.BMap.Polygon(trackPoint, {
strokeColor: '#CE0000',
strokeWeight: 4,
strokeOpacity: 1,
fillColor: '#ff8605',
fillOpacity:0.4
})
this.map.addOverlay(polygon);
}
render(){
const info = this.state.orderInfo || {};
return (
基础信息
-
用车模式
{info.mode == 1 ?'服务区':'停车点'}
-
订单编号
{info.order_sn}
-
车辆编号
{info.bike_sn}
-
用户姓名
{info.user_name}
-
手机号码
{info.mobile}
行驶轨迹
-
行程起点
{info.start_location}
-
行程终点
{info.end_location}
-
行驶里程
{info.distance/1000}公里
);
}
}
detail.less
@import '../../style/default';
@import '../../style/common';
.detail-items{
margin-left:90px;
padding:25px 50px 25px 0;
border-bottom:1px solid @colorN;
&:last-child{
border-bottom:none;
}
.item-title{
margin:20px 0;
font-size:@fontG;
color:@colorU;
}
.detail-form{
li{
.clearfix;
margin:20px 0;
line-height:20px;
font-size:15px;
color:@colorC;
}
}
.detail-form-left{
float:left;
width:164px;
text-align:right;
color:@colorH;
}
.detail-form-content{
padding-left:194px;
}
}
.order-map{
height: 450px;
margin: 25px -31px 0;
}
引入百度地图
index.html
React App
You need to enable JavaScript to run this app.
加载中,请稍后...
(第10章 项目工程化开发&)
第10章 项目工程化开发&
src\components\BaseForm\index.js
通过create()(FilterForm)可以实现数据双向绑定有getFieldsValue()方法
this.props.filterSubmit(fieldsValue)调用父级定义的方法,把获取的字段传递回去。
封装下拉框选项,封装表单元素公共BaseForm,时间范围查询
import React from 'react'
import { Input, Select, Form, Button, Checkbox, Radio, DatePicker} from 'antd'
import Utils from '../../utils/utils';
const FormItem = Form.Item;
const Option = Select.Option;
class FilterForm extends React.Component{
handleFilterSubmit = ()=>{
let fieldsValue = this.props.form.getFieldsValue();
this.props.filterSubmit(fieldsValue);
}
reset = ()=>{
this.props.form.resetFields();
}
initFormList = ()=>{
const { getFieldDecorator } = this.props.form;
const formList = this.props.formList;
const formItemList = [];
if (formList && formList.length>0){
formList.forEach((item,i)=>{
let label = item.label;
let field = item.field;
let initialValue = item.initialValue || '';
let placeholder = item.placeholder;
let width = item.width;
if (item.type == '时间查询'){
const begin_time =
{
getFieldDecorator('begin_time')(
)
}
;
formItemList.push(begin_time)
const end_time =
{
getFieldDecorator('end_time')(
)
}
;
formItemList.push(end_time)
}else if(item.type == 'INPUT'){
const INPUT =
{
getFieldDecorator([field],{
initialValue: initialValue
})(
)
}
;
formItemList.push(INPUT)
} else if (item.type == 'SELECT') {
const SELECT =
{
getFieldDecorator([field], {
initialValue: initialValue
})(
{ width: width }}
placeholder={placeholder}
{Utils.getOptionList(item.list)}
)
}
;
formItemList.push(SELECT)
} else if (item.type == 'CHECKBOX') {
const CHECKBOX =
{
getFieldDecorator([field], {
valuePropName: 'checked',
initialValue: initialValue //true | false
})(
{label}
)
}
;
formItemList.push(CHECKBOX)
}
})
}
return formItemList;
}
render(){
return (
{ this.initFormList() }
{ margin: '0 20px' }} onClick={this.handleFilterSubmit}查询
重置
);
}
}
export default Form.create({})(FilterForm);
src\pages\order\index.js
import React from 'react';
import { Card, Button, Table, Form, Select, Modal, DatePicker, message} from 'antd'
import axios from '../../axios'
import Utils from '../../utils/utils'
import BaseForm from '../../components/BaseForm'
const FormItem = Form.Item;
const Option = Select.Option;
export default class Order extends React.Component{
state = {
orderInfo:{},
orderConfirmVisble:false
}
params = {
page: 1
}
formList = [
{
type:'SELECT',
label:'城市',
field:'city',
placeholder:'全部',
initialValue:'1',
width:80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '北京' }, { id: '2', name: '天津' }, { id: '3', name: '上海' }]
},
{
type: '时间查询'
},
{
type: 'SELECT',
label: '订单状态',
field:'order_status',
placeholder: '全部',
initialValue: '1',
width: 80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '进行中' }, { id: '2', name: '结束行程' }]
}
]
componentDidMount(){
this.requestList()
}
handleFilter = (params)=>{
this.params = params;
this.requestList();
}
requestList = ()=>{
let _this = this;
axios.ajax({
url:'/order/list',
data:{
params: this.params
}
}).then((res)=>{
let list = res.result.item_list.map((item, index) => {
item.key = index;
return item;
});
this.setState({
list,
pagination: Utils.pagination(res, (current) => {
_this.params.page = current;
_this.requestList();
})
})
})
}
// 订单结束确认
handleConfirm = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请选择一条订单进行结束'
})
return;
}
axios.ajax({
url:'/order/ebike_info',
data:{
params:{
orderId: item.id
}
}
}).then((res)=>{
if(res.code ==0 ){
this.setState({
orderInfo:res.result,
orderConfirmVisble: true
})
}
})
}
// 结束订单
handleFinishOrder = ()=>{
let item = this.state.selectedItem;
axios.ajax({
url: '/order/finish_order',
data: {
params: {
orderId: item.id
}
}
}).then((res) => {
if (res.code == 0) {
message.success('订单结束成功')
this.setState({
orderConfirmVisble: false
})
this.requestList();
}
})
}
onRowClick = (record, index) => {
let selectKey = [index];
this.setState({
selectedRowKeys: selectKey,
selectedItem: record
})
}
openOrderDetail = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请先选择一条订单'
})
return;
}
window.open(`/#/common/order/detail/${item.id}`,'_blank')
}
render(){
const columns = [
{
title:'订单编号',
dataIndex:'order_sn'
},
{
title: '车辆编号',
dataIndex: 'bike_sn'
},
{
title: '用户名',
dataIndex: 'user_name'
},
{
title: '手机号',
dataIndex: 'mobile'
},
{
title: '里程',
dataIndex: 'distance',
render(distance){
return distance/1000 + 'Km';
}
},
{
title: '行驶时长',
dataIndex: 'total_time'
},
{
title: '状态',
dataIndex: 'status'
},
{
title: '开始时间',
dataIndex: 'start_time'
},
{
title: '结束时间',
dataIndex: 'end_time'
},
{
title: '订单金额',
dataIndex: 'total_fee'
},
{
title: '实付金额',
dataIndex: 'user_pay'
}
]
const formItemLayout = {
labelCol:{span:5},
wrapperCol:{span:19}
}
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
type: 'radio',
selectedRowKeys
}
return (
{marginTop:10}}
订单详情
{marginLeft:10}} onClick={this.handleConfirm}结束订单
{
return {
onClick: () => {
this.onRowClick(record, index);
}
};
}}
/>
{
this.setState({
orderConfirmVisble:false
})
}}
onOk={this.handleFinishOrder}
width={600}
>
{this.state.orderInfo.bike_sn}
{this.state.orderInfo.battery + '%'}
{this.state.orderInfo.start_time}
{this.state.orderInfo.location}
);
}
}
src\axios\index.js
axios请求封装
import JsonP from 'jsonp'
import axios from 'axios'
import { Modal } from 'antd'
export default class Axios {
static jsonp(options) {
return new Promise((resolve, reject) => {
JsonP(options.url, {
param: 'callback'
}, function (err, response) {
if (response.status == 'success') {
resolve(response);
} else {
reject(response.messsage);
}
})
})
}
static ajax(options){
let loading;
if (options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
loading.style.display = 'block';
}
let baseApi = 'https://www.easy-mock.com/mock/5a7278e28d0c633b9c4adbd7/api';
return new Promise((resolve,reject)=>{
axios({
url:options.url,
method:'get',
baseURL:baseApi,
timeout:5000,
params: (options.data && options.data.params) || ''
}).then((response)=>{
if (options.data && options.data.isShowLoading !== false) {
loading = document.getElementById('ajaxLoading');
loading.style.display = 'none';
}
if (response.status == '200'){
let res = response.data;
if (res.code == '0'){
resolve(res);
}else{
Modal.info({
title:"提示",
content:res.msg
})
}
}else{
reject(response.data);
}
})
});
}
}
(第11章 员工管理菜单模块开发&)
第11章 员工管理菜单模块开发&
src\components\ETable\index.js
import React from 'react'
import Utils from '../../utils/utils'
import {Table} from 'antd'
import "./index.less"
export default class ETable extends React.Component {
state = {}
//处理行点击事件
onRowClick = (record, index) => {
let rowSelection = this.props.rowSelection;
if(rowSelection == 'checkbox'){
let selectedRowKeys = this.props.selectedRowKeys;
let selectedIds = this.props.selectedIds;
let selectedItem = this.props.selectedItem || [];
if (selectedIds) {
const i = selectedIds.indexOf(record.id);
if (i == -1) {//避免重复添加
selectedIds.push(record.id)
selectedRowKeys.push(index);
selectedItem.push(record);
}else{
selectedIds.splice(i,1);
selectedRowKeys.splice(i,1);
selectedItem.splice(i,1);
}
} else {
selectedIds = [record.id];
selectedRowKeys = [index]
selectedItem = [record];
}
this.props.updateSelectedItem(selectedRowKeys,selectedItem || {},selectedIds);
}else{
let selectKey = [index];
const selectedRowKeys = this.props.selectedRowKeys;
if (selectedRowKeys && selectedRowKeys[0] == index){
return;
}
this.props.updateSelectedItem(selectKey,record || {});
}
};
// 选择框变更
onSelectChange = (selectedRowKeys, selectedRows) => {
let rowSelection = this.props.rowSelection;
const selectedIds = [];
if(rowSelection == 'checkbox'){
selectedRows.map((item)=>{
selectedIds.push(item.id);
});
this.setState({
selectedRowKeys,
selectedIds:selectedIds,
selectedItem: selectedRows[0]
});
}
this.props.updateSelectedItem(selectedRowKeys,selectedRows[0],selectedIds);
};
onSelectAll = (selected, selectedRows, changeRows) => {
let selectedIds = [];
let selectKey = [];
selectedRows.forEach((item,i)=> {
selectedIds.push(item.id);
selectKey.push(i);
});
this.props.updateSelectedItem(selectKey,selectedRows[0] || {},selectedIds);
}
getOptions = () => {
let p = this.props;
const name_list = {
"订单编号":170,
"车辆编号":80,
"手机号码":96,
"用户姓名":70,
"密码":70,
"运维区域":300,
"车型":42,
"故障编号":76,
"代理商编码":97,
"角色ID":64
};
if (p.columns && p.columns.length > 0) {
p.columns.forEach((item)=> {
//开始/结束 时间
if(!item.title){
return
}
if(!item.width){
if(item.title.indexOf("时间") > -1 && item.title.indexOf("持续时间") < 0){
item.width = 132
}else if(item.title.indexOf("图片") > -1){
item.width = 86
}else if(item.title.indexOf("权限") > -1 || item.title.indexOf("负责城市") > -1){
item.width = '40%';
item.className = "text-left";
}else{
if(name_list[item.title]){
item.width = name_list[item.title];
}
}
}
item.bordered = true;
});
}
const { selectedRowKeys } = this.props;
const rowSelection = {
type: 'radio',
selectedRowKeys,
onChange: this.onSelectChange,
onSelect:(record, selected, selectedRows)=>{
console.log('...')
},
onSelectAll:this.onSelectAll
};
let row_selection = this.props.rowSelection;
// 当属性未false或者null时,说明没有单选或者复选列
if(row_selection===false || row_selection === null){
row_selection = false;
}else if(row_selection == 'checkbox'){
//设置类型未复选框
rowSelection.type = 'checkbox';
}else{
//默认未单选
row_selection = 'radio';
}
return ({
onClick: ()=>{
if(!row_selection){
return;
}
this.onRowClick(record,index)
}
})}
/>
};
render = () => {
return (
{this.getOptions()}
)
}
}
src\components\BaseForm\index.js
封装公共BaseForm table查询栏目,form.getFieldsValue获取表单数据
import React from 'react'
import { Input, Select, Form, Button, Checkbox, Radio, DatePicker} from 'antd'
import Utils from '../../utils/utils';
const FormItem = Form.Item;
const Option = Select.Option;
class FilterForm extends React.Component{
handleFilterSubmit = ()=>{
let fieldsValue = this.props.form.getFieldsValue();
this.props.filterSubmit(fieldsValue);
}
reset = ()=>{
this.props.form.resetFields();
}
initFormList = ()=>{
const { getFieldDecorator } = this.props.form;
const formList = this.props.formList;
const formItemList = [];
if (formList && formList.length>0){
formList.forEach((item,i)=>{
let label = item.label;
let field = item.field;
let initialValue = item.initialValue || '';
let placeholder = item.placeholder;
let width = item.width;
if (item.type == '时间查询'){
const begin_time =
{
getFieldDecorator('begin_time')(
)
}
;
formItemList.push(begin_time)
const end_time =
{
getFieldDecorator('end_time')(
)
}
;
formItemList.push(end_time)
}else if(item.type == 'INPUT'){
const INPUT =
{
getFieldDecorator([field],{
initialValue: initialValue
})(
)
}
;
formItemList.push(INPUT)
} else if (item.type == 'SELECT') {
const SELECT =
{
getFieldDecorator([field], {
initialValue: initialValue
})(
{ width: width }}
placeholder={placeholder}
{Utils.getOptionList(item.list)}
)
}
;
formItemList.push(SELECT)
} else if (item.type == 'CHECKBOX') {
const CHECKBOX =
{
getFieldDecorator([field], {
valuePropName: 'checked',
initialValue: initialValue //true | false
})(
{label}
)
}
;
formItemList.push(CHECKBOX)
}
})
}
return formItemList;
}
render(){
return (
{ this.initFormList() }
{ margin: '0 20px' }} onClick={this.handleFilterSubmit}查询
重置
);
}
}
export default Form.create({})(FilterForm);
utils.js
import React from 'react';
import { Select } from 'antd'
const Option = Select.Option;
export default {
formateDate(time){
if(!time)return '';
let date = new Date(time);
return date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+' '+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds();
},
pagination(data,callback){
return {
onChange:(current)=>{
callback(current)
},
current:data.result.page,
pageSize:data.result.page_size,
total: data.result.total_count,
showTotal:()=>{
return `共${data.result.total_count}条`
},
showQuickJumper:true
}
},
// 格式化金额,单位:分(eg:430分=4.30元)
formatFee(fee, suffix = '') {
if (!fee) {
return 0;
}
return Number(fee).toFixed(2) + suffix;
},
// 格式化公里(eg:3000 = 3公里)
formatMileage(mileage, text) {
if (!mileage) {
return 0;
}
if (mileage >= 1000) {
text = text || " km";
return Math.floor(mileage / 100) / 10 + text;
} else {
text = text || " m";
return mileage + text;
}
},
// 隐藏手机号中间4位
formatPhone(phone) {
phone += '';
return phone.replace(/(\d{3})\d*(\d{4})/g, '$1***$2')
},
// 隐藏身份证号中11位
formatIdentity(number) {
number += '';
return number.replace(/(\d{3})\d*(\d{4})/g, '$1***********$2')
},
getOptionList(data){
if(!data){
return [];
}
let options = [] //[全部];
data.map((item)=>{
options.push({item.name})
})
return options;
},
/**
* ETable 行点击通用函数
* @param {*选中行的索引} selectedRowKeys
* @param {*选中行对象} selectedItem
*/
updateSelectedItem(selectedRowKeys, selectedRows, selectedIds) {
if (selectedIds) {
this.setState({
selectedRowKeys,
selectedIds: selectedIds,
selectedItem: selectedRows
})
} else {
this.setState({
selectedRowKeys,
selectedItem: selectedRows
})
}
},
}
code\src\pages\order\index.js
import React from 'react';
import { Card, Button, Table, Form, Select, Modal, DatePicker, message} from 'antd'
import axios from '../../axios'
import Utils from '../../utils/utils'
import BaseForm from '../../components/BaseForm'
const FormItem = Form.Item;
const Option = Select.Option;
export default class Order extends React.Component{
state = {
orderInfo:{},
orderConfirmVisble:false
}
params = {
page: 1
}
formList = [
{
type:'SELECT',
label:'城市',
field:'city',
placeholder:'全部',
initialValue:'1',
width:80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '北京' }, { id: '2', name: '天津' }, { id: '3', name: '上海' }]
},
{
type: '时间查询'
},
{
type: 'SELECT',
label: '订单状态',
field:'order_status',
placeholder: '全部',
initialValue: '1',
width: 80,
list: [{ id: '0', name: '全部' }, { id: '1', name: '进行中' }, { id: '2', name: '结束行程' }]
}
]
componentDidMount(){
this.requestList()
}
handleFilter = (params)=>{
this.params = params;
this.requestList();
}
requestList = ()=>{
let _this = this;
axios.ajax({
url:'/order/list',
data:{
params: this.params
}
}).then((res)=>{
let list = res.result.item_list.map((item, index) => {
item.key = index;
return item;
});
this.setState({
list,
pagination: Utils.pagination(res, (current) => {
_this.params.page = current;
_this.requestList();
})
})
})
}
// 订单结束确认
handleConfirm = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请选择一条订单进行结束'
})
return;
}
axios.ajax({
url:'/order/ebike_info',
data:{
params:{
orderId: item.id
}
}
}).then((res)=>{
if(res.code ==0 ){
this.setState({
orderInfo:res.result,
orderConfirmVisble: true
})
}
})
}
// 结束订单
handleFinishOrder = ()=>{
let item = this.state.selectedItem;
axios.ajax({
url: '/order/finish_order',
data: {
params: {
orderId: item.id
}
}
}).then((res) => {
if (res.code == 0) {
message.success('订单结束成功')
this.setState({
orderConfirmVisble: false
})
this.requestList();
}
})
}
onRowClick = (record, index) => {
let selectKey = [index];
this.setState({
selectedRowKeys: selectKey,
selectedItem: record
})
}
openOrderDetail = ()=>{
let item = this.state.selectedItem;
if (!item) {
Modal.info({
title: '信息',
content: '请先选择一条订单'
})
return;
}
window.open(`/#/common/order/detail/${item.id}`,'_blank')
}
render(){
const columns = [
{
title:'订单编号',
dataIndex:'order_sn'
},
{
title: '车辆编号',
dataIndex: 'bike_sn'
},
{
title: '用户名',
dataIndex: 'user_name'
},
{
title: '手机号',
dataIndex: 'mobile'
},
{
title: '里程',
dataIndex: 'distance',
render(distance){
return distance/1000 + 'Km';
}
},
{
title: '行驶时长',
dataIndex: 'total_time'
},
{
title: '状态',
dataIndex: 'status'
},
{
title: '开始时间',
dataIndex: 'start_time'
},
{
title: '结束时间',
dataIndex: 'end_time'
},
{
title: '订单金额',
dataIndex: 'total_fee'
},
{
title: '实付金额',
dataIndex: 'user_pay'
}
]
const formItemLayout = {
labelCol:{span:5},
wrapperCol:{span:19}
}
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
type: 'radio',
selectedRowKeys
}
return (
{marginTop:10}}
订单详情
{marginLeft:10}} onClick={this.handleConfirm}结束订单
{
return {
onClick: () => {
this.onRowClick(record, index);
}
};
}}
/>
{
this.setState({
orderConfirmVisble:false
})
}}
onOk={this.handleFinishOrder}
width={600}
>
{this.state.orderInfo.bike_sn}
{this.state.orderInfo.battery + '%'}
{this.state.orderInfo.start_time}
{this.state.orderInfo.location}
);
}
}
src\pages\user\users
Card增删改按钮列表样式
Table字典转换,对象object索引获取值,getFieldDecorator双向数据绑定,wrappedComponentRef绑定form内容到本地作用域
重置表单内容,selectItem绑定选择的行数据,moment日期,查看详情字典转换
ETable表格封装,增删改查功能实现,this作用域
import React from 'react'
import { Card, Button, Table, Form, Input, Checkbox,Select,Radio, Icon, message, Modal, DatePicker } from 'antd'
import axios from '../../axios/index'
import Utils from '../../utils/utils'
import ETable from '../../components/ETable/index'
import Moment from 'moment'
const FormItem = Form.Item;
const Option = Select.Option;
const RadioGroup = Radio.Group;
export default class User extends React.Component{
state = {
list:[]
}
params = {
page:1
}
requestList = ()=>{
axios.ajax({
url:'/table/list1',
data:{
params:{
page:this.params.page
}
}
}).then((res)=>{
let _this = this;
this.setState({
list:res.result.list.map((item,index)=>{
item.key=index
return item;
}),
pagination:Utils.pagination(res,(current)=>{
_this.params.page = current;
_this.requestList();
})
})
})
}
componentDidMount(){
this.requestList();
}
// 操作员工
handleOperator = (type)=>{
let item = this.state.selectedItem;
if(type =='create'){
this.setState({
title:'创建员工',
isVisible:true,
type
})
}else if(type=="edit" || type=='detail'){
if(!item){
Modal.info({
title: '信息',
content: '请选择一个用户'
})
return;
}
this.setState({
title:type=='edit'?'编辑用户':'查看详情',
isVisible:true,
userInfo:item,
type
})
}else if(type=="delete"){
if(!item){
Modal.info({
title: '信息',
content: '请选择一个用户'
})
return;
}
Utils.ui.confirm({
text:'确定要删除此用户吗?',
onOk:()=>{
axios.ajax({
url:'/user/delete',
data:{
params:{
id:item.id
}
}
}).then((res)=>{
if(res.code ==0){
this.setState({
isVisible:false
})
this.requestList();
}
})
}
})
}
}
handleSubmit = ()=>{
let type = this.state.type;
let data = this.userForm.props.form.getFieldsValue();
axios.ajax({
url:type == 'create'?'/user/add':'/user/edit',
data:{
params:{
...data
}
}
}).then((res)=>{
if(res.code ==0){
this.setState({
isVisible:false
})
this.requestList();
}
})
}
render(){
const columns = [{
title: 'id',
dataIndex: 'id'
}, {
title: '用户名',
dataIndex: 'username'
}, {
title: '性别',
dataIndex: 'sex',
render(sex){
return sex ==1 ?'男':'女'
}
}, {
title: '状态',
dataIndex: 'state',
render(state){
let config = {
'1':'咸鱼一条',
'2':'风华浪子',
'3':'北大才子一枚',
'4':'百度FE',
'5':'创业者'
}
return config[state];
}
},{
title: '爱好',
dataIndex: 'interest',
render(interest){
let config = {
'1':'游泳',
'2':'打篮球',
'3':'踢足球',
'4':'跑步',
'5':'爬山',
'6':'骑行',
'7':'桌球',
'8':'麦霸'
}
return config[interest];
}
},{
title: '爱好',
dataIndex: 'isMarried',
render(isMarried){
return isMarried?'已婚':'未婚'
}
},{
title: '生日',
dataIndex: 'birthday'
},{
title: '联系地址',
dataIndex: 'address'
},{
title: '早起时间',
dataIndex: 'time'
}
];
return (
登 录
{marginTop:10}}
this.handleOperator('create')}>创建员工
this.handleOperator('edit')}>编辑员工
this.handleOperator('detail')}>员工详情
this.handleOperator('delete')}>删除员工
{
this.userForm.props.form.resetFields();
this.setState({
isVisible:false,
userInfo:''
})
}}
>
this.userForm = inst }/>
);
}
}
class UserForm extends React.Component{
getState = (state)=>{
return {
'1':'咸鱼一条',
'2':'风华浪子',
'3':'北大才子一枚',
'4':'百度FE',
'5':'创业者'
}[state]
}
render(){
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: {span: 5},
wrapperCol: {span: 16}
};
const userInfo = this.props.userInfo || {};
const type = this.props.type;
return (
{
userInfo && type=='detail'?userInfo.username:
getFieldDecorator('user_name',{
initialValue:userInfo.username
})(
)
}
{
userInfo && type=='detail'?userInfo.sex==1?'男':'女':
getFieldDecorator('sex',{
initialValue:userInfo.sex
})(
男
女
)}
{
userInfo && type=='detail'?this.getState(userInfo.state):
getFieldDecorator('state',{
initialValue:userInfo.state
})(
咸鱼一条
风华浪子
北大才子一枚
百度FE
创业者
)}
{
userInfo && type=='detail'?userInfo.birthday:
getFieldDecorator('birthday',{
initialValue:Moment(userInfo.birthday)
})(
)}
{
userInfo && type=='detail'?userInfo.address:
getFieldDecorator('address',{
initialValue:userInfo.address
})(
)}
);
}
}
UserForm = Form.create({})(UserForm);
Card增删改按钮列表样式
common.less
@import './default.less';
@import './loading.less';
ul,li{
list-style: none;
}
.clearfix{
&::after{
content:' ';
clear:both;
display: block;
visibility: hidden;
}
}
.container{
.nav-left{
background-color:#001529;
color: #ffffff;
height: calc(100vh);
}
.main{
height: calc(100vh);
background-color: @colorL;
overflow: auto;
}
.content{
position: relative;
padding: 20px;
}
}
.content-wrap{
background: #ffffff;
border: 1px solid #e8e8e8;
margin-top: -3px;
.ant-table-wrapper{
margin-left: -1px;
margin-right: -2px;
}
}
.ant-card{
button{
margin-right: 10px;
}
}
src\axios\index.js
import JsonP from 'jsonp'
import axios from 'axios'
import { Modal } from 'antd'
export default class Axios {
static jsonp(options) {
return new Promise((resolve, reject) => {
JsonP(options.url, {
param: 'callback'
}, function (err, response) {
if (response.status == 'success') {
resolve(response);
} else {
reject(response.messsage);
}
})
})
}
static ajax(options){
let loading;
if (options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
loading.style.display = 'block';
}
let baseApi = 'https://www.easy-mock.com/mock/5a7278e28d0c633b9c4adbd7/api';
return new Promise((resolve,reject)=>{
axios({
url:options.url,
method:'get',
baseURL:baseApi,
timeout:5000,
params: (options.data && options.data.params) || ''
}).then((response)=>{
if (options.data && options.data.isShowLoading !== false) {
loading = document.getElementById('ajaxLoading');
loading.style.display = 'none';
}
if (response.status == '200'){
let res = response.data;
if (res.code == '0'){
resolve(res);
}else{
Modal.info({
title:"提示",
content:res.msg
})
}
}else{
reject(response.data);
}
})
});
}
}
(第12章 车辆地图模块开发&)
第12章 车辆地图模块开发&
(第13章 图表模块开发&)
第13章 图表模块开发&
(第14章 权限设置模块开发&)
第14章 权限设置模块开发&
(第15章 Redux集成开发&)
第15章 Redux集成开发&