项目实战二:共享单车后台2
今天听雷鹏飞大佬讲了一个 material UI 记录一下 跟antd差不多
https://www.easy-mock.com/login 提供动态数据渲染 Easy mock
mockjs搭建的平台)
强大的插件Mock.js,可以非常方便的模拟后端的数据,也可以轻松的实现增删改查这些操作。
ps:操作时间码转换成统一格式
const columns=[
{title:'...',
dataIndex:'...',
render:Utils.formateDate
}]formateDate(time){if(!time)return '';let date= new Date(time);return date.getFullYear() + '-'+(data.getMouth()+1)+'-'+date.get.....;
}数据
{"code":0,//成功时返回
"msg":"",//错误时返回信息 等同于"message"
"result|10":[{ //|10指的是定义10条数据id:'0',userName:'Jack',...
},
...
],
page:1,
page_size:10,
}{"code":0,//成功时返回
"msg":"",//错误时返回信息 等同于"message"
"result":{ //|10指的是定义10条数据"list|5":[{"id|+1":1, //id值每次递增+1"userName":“@cname",//自动获取中文名"sex|1-2":1,//性别只有1、2
...}],
page:1,//第一页
page_size:10,//1页放10条
total:100//100页}
}
表格
pages/tabke
basic.js
src/config/menuConfig.js 定义路由地址 名字 等
menuConfig.js
export default [{title:'首页',key:'/admin/home'},{ title:'UI',key:'admin/ui',children:[{title:'按钮',key:'url',},//一个大括号里是一个人的信息...]},...
]basic.js
...
state={//state里数据用来渲染Htmldom结构的 执行setState会重新渲染domdataSource2:[]//dataSource2如果不存入state render不执行
}
params={//只改变页码不需要改变dom结构page:1 //接口调用需要 而不是页面需要
}
componentDidMount(){const dataSource =[//数据源{id:'0',userName:'Jack'sex:'1',...}]data.map((item,index)=>{item.key = index;}) //datasource在表格中应有独一无二的keythis.setState({dataSource//相当于dataSource:dataSource})this.request();
}request=() =>{ //动态获取mock数据let baseUrl = 'url';axios.get(baseUrl +'/table/list').then((res)=>{//.then...就是一个promise的结构if(res.status =='200'&&res.data.code==0){this.setState({dataSource2:res.data.result,selectedRowKeys:[],selectedRows:null})}})
}
onRowClick =(record,index) =>{let selectKey = [index];//多选会用到这个数组this.setState({selectedRowKeys:selectKey,//选中索引selectedItem:record//选择某一行})
}
//多选执行删除动作
handleDelete =()=>{let row = this.state.selectedRows;let ids= [];rows.map((item)=>{ids.push(item.id)})Modal.confirm({title:'...',content:`...${ids.join(',')`,//用,连接起来ids里面的值onOk:()=>{message.success('...');this.request();//刷新一下}})}
render () {const columns =[//定义表头{title:'id',//列名dataIndex:'id'//数据项中所对应的key},{title:'用户名',//列名dataIndex:'userName'},{title:'性别',//列名dataIndex:'sex',render(sex){return sex==1 ? '男':'女'}},{title:'状态',dataIndex:'state',render(abc){//或者statelet config ={//太多了的话也可以专门定义一个字典文件.js'1':'咸鱼',....}return config[abc];//或者state}还有一种写法:render(abc){//或者statereturn {'1':'咸鱼','2':'风华浪子',...}[abc];//或者state}}...]const { selectedRowKeys } =this.state;//等于const selectedRowKeys =this.state.selectedRowKeys ;const rowSelection = {type:'radio',//复选为checkboxselectedRowKeys//存选中某一项的key//也可以加onchange:意为选中下一个框时发生了改变启动事件}const rowSelection = {type:'checkbox',//复选为checkboxselectedRowKeys,//存选中某一项的keyonChange:(selectedRowKeys,selectedRows)=>{//rows指行let ids =[];selectedRows.map((item)=>{ids.push(item.id)})this.setState({selectedRowKeys,selectedIds:ids//存下对应的id})}}return(<div><Card title="基础表格"><Table bordered //边框columns={columns}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource}pagination={false}//不分页/></Card><Card title="动态数据渲染表格" style={{margin:'10px 0' }}><Table bordered //边框columns={columns}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={false}//不分页/></Card><Card title="Mock-单选" style={{margin:'10px 0' }}><div><Button onClick={}this.handleDelete}>删除</Button></div><Table onRow={(record,index) => {return {onClick: () => {this.onRowClick(record,index);} // 点击行即可选中};bordered //边框rowSelection={rowSelection}//设置为单选columns={columns}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={false}//不分页/></Card><Card title="Mock-分页" style={{margin:'10px 0' }}><Table bordered //边框columns={columns}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={this.state.pagination}//把接口里数据存下来/></Card></div>)}
src/axios/index.js
...
//请求插件的封装
static ajax(options) { //resolve成功时返回 reject失败时返回let loading;if(options.data && options.data.isShowLoading !== false){//不是false就showloading = document.getElementById('ajaxLoading'); //loading代码在全局html文件里 //直接用这种方式获取loading.style.display = 'block';}let baseApi= 'url';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){//不是false就showloading = document.getElementById('ajaxLoading'); //loading代码在全局html文件里 //直接用这种方式获取loading.style.display = 'none';//关闭掉}if(response.status == '200'){//HTTP状态请求码let res= response.data;if(res.code == '0') {//业务代码成功定义为0resolve(res);//把res.data抛出去 我们可以接收到}else{Modal.info({ //错误信息title:"提示",content:res.msg})}}else {reject(response.data);}//把它打印出来并报错})});}封装了之后我们可以修改上述的request 即
request =() =>{let _this =this;//怕作用域出问题 提前提取thisaxios.ajax({url:'/table/list',data:{params:{ page:this.params.page},//第一页的isShowLoading:false//不需要show loading}}).then((res)=>{if(res.code == 0){res.result.list.map((item,index)=>{item.key= index;})this.setState({dataSource2:res.result.list,selectedRowKeys:[],selectedRows:null,pagination:Utils.pagination(res,(current)=>{_this.params.page = current;//把current存入this.params.page里this.request();})})}})
}
然后要做一个在请求完成之前的loading效果
public/index.html
....
<div id="root"></div>//插入以下代码
<div class="ajax-loading" id="ajaxLoading" style="display:none;"><div class="overlay"></div><div class="loading"><img src="url" alt=" "><span>加载中</span></div>
</div>还有性别等里面显示的是数字 我们希望显示文字 修改一下columns
那如果想在打开的网页中修改表格数据呢?----
分页功能
utils.js
export default{...,//关于分页的封装//当点击下一页时触发回调函数 pagination(data,callback){let page = {//current当前页数 onchange为页码改变的回调,参数是改变后的页码及每页条数onChange:(current)=>{callback(current);},current:data.result.page,pageSize:data.result.page_size,total:dada.result.total,showTotal:()=>{//用于显示数据总量和当前数据顺序return `共${data.result.total}条`},showQuickJumper:true//showQuickJumper是否可以快速跳转至某页}return page;//或者 整个return {...}即可},getOptionList(data){if(!data){return [];}let option = [];data.map((item)=>{options.push(<Option value={item.id} key={item.id}>{item.name}</Option>)})return options;//不要忘记返回}
}
高级表格:分别是表头固定和左侧固定
pages/table/highTable.js
handleChange =(pagination, filters, sorter)=>{this.setState({sortOrder:sorter.order})
}
handleDelete =(item) =>{let id =item.id;Modal.confirm({title:'确认',content:'确认删除?',onOk:()=>{message.success('删除成功');this.request();}})
}
render () {const columns =[//定义表头{title:'id',//列名key:'id',width:80,//这一列宽度为80dataIndex:'id'//数据项中所对应的key},{title:'用户名',//列名dataIndex:'userName'},{title:'性别',//列名dataIndex:'sex',render(sex){return sex==1 ? '男':'女'}},{title:'状态',dataIndex:'state',render(abc){//或者statelet config ={//太多了的话也可以专门定义一个字典文件.js'1':'咸鱼',....}return config[abc];//或者state}}...]const columns2 =[//定义表头{title:'id',//列名key:'id',width:80,//这一列宽度为80fixed:'left', //该列左侧固定不动dataIndex:'id'//数据项中所对应的key},{title:'用户名',//列名dataIndex:'userName'},{title:'性别',//列名dataIndex:'sex',render(sex){return sex==1 ? '男':'女'}},{title:'状态',dataIndex:'state',render(abc){//或者statelet config ={//太多了的话也可以专门定义一个字典文件.js'1':'咸鱼',....}return config[abc];//或者state}}...]const columns3 =[//定义表头{title:'id',//列名key:'id',width:80,//这一列宽度为80dataIndex:'id'//数据项中所对应的key},{title:'用户名',//列名dataIndex:'userName'},{title:'年龄',//列名key:'age',dataIndex:'age',width:80,sorter:(a,b)=>{return a.age -b.age; //小于0则a<b 即为升序},sortOrder:this.state.sortOrder},{title:'性别',//列名dataIndex:'sex',render(sex){return sex==1 ? '男':'女'}},{title:'状态',dataIndex:'state',render(abc){//或者statelet config ={//太多了的话也可以专门定义一个字典文件.js'1':'咸鱼',....}return config[abc];//或者state}}...]const columns4 =[//定义表头{title:'id',//列名列宽度为80dataIndex:'id'//数据项中所对应的key},{title:'用户名',//列名dataIndex:'userName'},{title:'年龄',//列名dataIndex:'age',width:80,},{title:'性别',//列名dataIndex:'sex',render(sex){return sex==1 ? '男':'女'}},{title:'状态',dataIndex:'state',render(abc){let config ={//badge 徽标'1':<Badge status="success" text="1"/>,//status :error default processing warning....}return config[abc];//或者state}},{title:'操作',render:(text,item)=>{//item指一行 改为箭头函数使this指向react里面return <Button size="small" onClick ={(item)=>{this.handleDelete(item)} }>删除</Button> }}...]return(){<div><Card title="头部固定"><Table bordered //边框columns={columns}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource}pagination={false}//不分页scroll={{y:240}}//在y轴滚动 :后为高度px //注意表头和表格要对齐 /></Card><Card title="左侧固定" style={{margin:'10px 0' }}><Table bordered //边框columns={columns2}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={false}//不分页scroll={{x:2650}}//在x轴滚动 :后为高度px 注意把每一列的宽度相加再多一点的值放到这//注意表头和表格要对齐 /></Card><Card title="表格排序" style={{margin:'10px 0' }}><Table bordered //边框columns={columns3}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={false}//不分页onChange={this.handleChange} //排序 分页都可用scroll={{x:2650}}//在x轴滚动 :后为高度px 注意把每一列的宽度相加再多一点的值放到这//注意表头和表格要对齐 /></Card><Card title="操作按钮" style={{margin:'10px 0' }}><Table bordered //边框columns={columns4}//如果放在render外需要用this.state.columns引用dataSource={this.state.dataSource2}pagination={false}//不分页scroll={{x:2650}}//在x轴滚动 :后为高度px 注意把每一列的宽度相加再多一点的值放到这//注意表头和表格要对齐 /></Card></div>}
城市开通
state ={ list:[],isShowOpenCity:false
}
page = {page:1
}componentDidMount(){this.requestList();
}
requestList ={ //获取接口中的list let _this= this;axios.ajax({url:'...',data:{params:{page:this.params.page}}}).then((res)=>{this.setState({list:res.result.item_list.map((item,index)=>{//为了加上独一的key值 不然报错item.key = index;return item;//返回一个全新的对象 不return还是之前那个老版的}),pagination:Utils.pagination(res,()=>{_this.params.page =current;_this.requestList();})})})
}
handleOpenCity =() =>{this.setState({isShowOpenCity:true})
}handleSubmit =()=>{let cityInfo =this.cityForm.props.form.getFieldsValue();axios.ajax({url:'url',data:{params:cityInfo}}).then((res)=>{if(res.code =='0'){message.success('开通成功');this.setState({isShowOpenCity:false})this.requestList();//更新列表}})
}
render(){const columns = [表格其中的一列 一格中数据不能渲染复杂的对象、数组等,只能渲染普通的如数字文字等,所以要修改数据源{title:'...',dataIndex:'...', //dataIndex传过来的值是一个数组render(arr){return arr.map((item)=>{return item.user_name;//得到全新的数组 数组中只有username}).join(',';//以逗号分开并连接成一个全新的字符串}},...]return(<div><Card><FilterForm /></Card><Card><Button type="primary" onClick={this.handleOpenCity} /> </Card><div className="content-wrap"><Table columns={columns}dataSource={this.state.list}pagination={this.state.pagination}/></div><Modaltitle="开通城市"visible={this.state.isShowOpenCity}onCancle={()=>{this.setState({isShowOpenCity:false})}}onOk={this.handleSubmit}><OpenCityForm wrappedComponentRef={(inst)=>{this.cityForm = inst;}}/>//wrappedComponentRef相当于ref 把表单对象存到本地 便于获取里面的值//再次引用时直接this.cityForm.props.from.getFieldValue();)
}
//注意是在一个文件里
class OpenCityForm extends React.Component{render(){const formItemLayout ={labelCol:{span:5},wrapperCol:{span:10}}const { getFieldDecorator } =this.props.form;//辅助性的帮助双向数据绑定return(<Form layout="horizontal" {...formItemLayout}><FormItem label="选择城市">{getFieldDecorator('city_id',{initialValue:'1'})(<Select><Option value="1">全部</Option><Option value="2">北京市</Option><Option value="3">天津市</Option></Select>)}</FormItem>...</Form>)}
}
OpenCityForm = Form.create({})(openCityForm);
订单管理
pages/order/index.js
const FormItem=Form.Item;
const Option = Select.Option;
state={}
params ={ page:1}
componentDidMount(){this.requestList()
}
requestList =()=>{axios.ajax({url:'/order/list',data:{params:{page:this.params}}}).then((res)=>{(//写法1let list = res.result.item_list.map((item,index) => {item.key = index;return item;});)if(res.code ==0)this.setState({list:res.result.item_list.map((item,index)=>{//为了加上独一的key值 不然报错item.key = index;return item;//返回一个全新的对象 不return还是之前那个老版的}),pagination:Utils.pagination(res,()=>{_this.params.page =current;_this.requestList();})})})
}handleFinishOrder =() =>
{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,orderConfirmVisible:true}),pagination:Utils.pagination(res,()=>{_this.params.page =current;_this.requestList();})})})}}openOrderDetail =()=>{let item = this.state.selectedItem;if(!item){//没选择一个行时Modal.info({title:'信息',content:'请选择一条订单结束'})return;}//新建页加载window.open(`/#/common/order/detail/${item.id}`,'_blank')//window.location.href = `/#/common/order/detail/${item.id}`同一页加载
}handleFinishOrder =() =>{}render(){const columns=[//表头{title:'订单编号',dataIndex:'order_sn'//要严格 遵守接口的返回名},{title:'车辆编号',dataIndex:'bike_sn'//要严格 遵守接口的返回名},{title:'用户名',dataIndex:'user_name'//要严格 遵守接口的返回名},{title:'手机号',dataIndex:'mobile'//要严格 遵守接口的返回名},{title:'里程',dataIndex:'distance'//要严格 遵守接口的返回名},。。。]return(<div><Card><FilterForm /></Card><Card><Button onClick={this.openOrderDetail>订单详情</Button><Button>结束订单</Button></Card><div className="content-wrap"><Tableborderedcolumns={columns}dataSource={this.state.list}pagination={this.state.pagination}//注意区分:datasource里面是数据 columns里面是标题以及它的索引和key值/></div></div>)
}<FilterForm />:
render(){const {getFieldDecorator}=this.props.form;return(<Form layout="inline"><FormItem label="城市">{getFieldDecorator('city_id')(<Selectstyle={{width:100}}placeholder="全部"><Option value=""> 全部</Option><Option value="1"> 北京市</Option>。。。</Select>))
}
</FormItem>
<Form layout="inline"><FormItem label="订单时间">{getFieldDecorator('start_time')(<DatePicker showTimen format="YYYY-MM-DD HH:mm:ss" />)} {getFieldDecorator('end_time')(<DatePicker style={{marginLeft:10}} showTimen format="YYYY-MM-DD HH:mm:ss" />)}
</FormItem>
通用页面的结构设计
router.js
import ....
export default class ....{render(){return(<HashRouter><App><Route path="/login" component={Login} /><Route path="/admin" render={()=><Admin>//都属于admin下面的子页面<Switch><Route path="/admin/ui/buttons" component={Buttons} />//子路由的嵌套<Route component={NoMatch} />//没匹配的都会跳转到404</Switch></Admin>} />//进入admin即可进入主页面</App></HashRouter>);}
}//通用的js文件承载大体不动的部分 比如页头页脚 。。只改变里面的内容部分
admin.js
import React from 'react'
import { Row } from 'antd';
import './style/common.less'
import Header from './components/Header'
import Footer from './components/Footer'
import NavLeft from './components/Navleft'
export default class Admin extends React.Component {render(){return (<Row className="container"><Col span="3" className="nav-left"><NavLeft /></Col><Col span="21" className="main"><Header /><Row className="content">{this.props.children}//动态实现 嵌套子组件</Row>//body区域<Footer /></Col></Row>)}
}
我们再建一个common.js作为类似一个admin.js 作为详情页面
common.js
<Row className="simple-page">//二级页面头部不一样 另取一个classname
<Header menuType="second" />//menuType //传了一个second值
</Row>
...在header/index.js
render(){const menuType =this.props.menuType;render(...{//menutype有值返回空 无值返回页头menuType?'':<div className="header"><Row className="header-top"> //行{menuType?<Col span="6"><img src="/assets/logo-ant.svg" alt="" /><span>IMooc 通用管理系统 </span></Col>:' '}<Col span={menuTyoe?18:24}><span>欢迎,{this.state.userName}</span><a href="#">退出</a></Col></Row><Row className="breadcrumb"><Col span="4" className="breadcrumb-title">首页</Col><Col span="20" className="weather"><span className="date">{this.state.sysTime}</span><span className="weather-img"><img src={this.state.dayPictureUrl} alt="" /></span><span className="weather-detail">{this.state.weather}</span></Col></Row></div>})}定义路由
<Route path="/common" render{() =><Common> <Route path="/common/order/detail/:orderId" component={OrderDetail}/>//:后加动态路由 变量为orderId</Common>
} />const info =this.state.orderInfo ||{};
//若为空则是一个{}
.clearfix//清除浮动
首先来看一下伪元素 ::after
https://www.cnblogs.com/starof/p/4459991.html
里面有清除浮动实例
https://www.cnblogs.com/937522zy/p/6650708.html
我们提取一个典型出来
.cf:before,
.cf:after {content: " ";//清空内容clear:both;//清除两边浮动display:block;visibility:hidden;//使元素不可见 不然会占位
}
//可在元素头/尾部自动清除浮动
地图功能实现
打开map.baidu.com
然后点击最下面的百度地图开放平台
开发文档 - javascript API
使用前应先申请ak密钥 申请成为开发者 登陆
创建应用
在public/index.html
在title下面把<script …><… />引入进来即可
BMap挂载在window下 window....来引用然后在detail.js里初始化地图 在render上面
通过this.renderMap(res.result);来调用它renderMap =(result)=>{this.map = new window.BMap.Map('要初始化的百度地图的div的id');this.map.centerAndZoom('北京',11);//BMap.Map.centerAndZoom()方法要求设置中心点坐标和地图级别。this.addMapControl();this.drawBikeRoute(result.position_list);this.drawServiceArea(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}));//比例尺 右上角
}
//绘制用户行驶路线 positionList坐标点
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);//lon经度 lat纬度let startIcon = new window.BMap.Icon('/assets/start_point.png',new window.BMap.Size(36,42),{//icon需要的空间大小imageSize:new window.BMap.Size(36,42), //图像大小anchor:new window.BMap.Size(36,42)//anchor图标的定位点相对于图标左上角的偏移值})//图标宽高let startMarker =new window.BMap.Marker(startPoint,{icon:startIcon});//将坐标点和图标对应起来 点在文档里面叫做markerthis.map.addOverlay(startMarker);//添加点endPoint = new window.BMap.Point(last.lon,last.lat);//lon经度 lat纬度let endIcon = new window.BMap.Icon('/assets/start_point.png',new window.BMap.Size(36,42),{//icon需要的空间大小imageSize:new window.BMap.Size(36,42), //图像大小anchor:new window.BMap.Size(36,42)//anchor图标的定位点相对于图标左上角的偏移值})let endMarker =new window.BMap.Marker(endPoint,{icon:endIcon});//将坐标点和图标对应起来 点在文档里面叫做markerthis.map.addOverlay(endMarker);//添加点//连接路线图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 polyline =new window.BMap.Polyline(trackPoint,{//折现属性 详情见http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html#a3b11strokeColor:'#FFFFFF',strokeWeight:3,strokeOpacity:1})this.map.addOverlay(polyline);this.map.centerAndZoom(endPoint,11);}}
drawServiceArea =(positionList) =>{//绘制服务区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:'#FFFFFF',strokeWeight:3,strokeOpacity:1,fillColor:'#ff8605',fillOpacity:0.4})this.map.addOverlay(polygon);
}render(){const info =this.state.orderInfo || {};return(<div><Card><div id="orderDetailMap" className="order-map">要初始化的百度地图的div</div> )
}
项目工程化
- 项目架构设计
- 目录结构定义
- 制定项目开发规范 (实例如https://www.cnblogs.com/luxiaoxiao/p/6432788.html) eslint
- 模块化 组件化
- 前后端接口规范
- 性能优化 自动化部署(压缩 合并 打包
对表单等基本组件进行封装
src/components/BaseForm 文件夹
- index.js
应用:
在order/index.js下测试
封装了之后就可以把之后的Filterform删掉了
formList = [{type:'SELECT',label:'城市',field:'city',placeholder:'全部',initialValue:'1',width:100,list:[{id:'0',name:'全部'},{id:'1',name:'北京'},{id:'2',name:'天津'},{id:'3',name:'上海'}]},{type:'时间查询',},{type:'SELECT',field:'order_status',label:'订单状态',placeholder:'全部',initialValue:'1',width:100,list:[{id:'0',name:'全部'},{id:'1',name:'进行中'},{id:'2',name:'结束行程'},]},]handleFilter = (params)=>{this.params = params;this.requestList();
}
return(<div><Card><BaseForm formList = { this.formList } filterSubmit={this.handleFilter} /> //formlist为了把该表单数据传给封装好的basicform </div>
)
BaseForm /index.jsconst FormItem =Form.Item;
const Option = Select.Option;class FilterForm extends React.Component
{handleFilterSubmit =() =>{let fieldsValue =this.props.form.getFieldValue();this.props.filterSubmit(fieldsValue); //调用那边本部的方法 这个不是固定的}reset =()=>{this.props.form.resetFields();}initFormList =()=>{const { getFieldDecorator } =this.props.form;//antd里有getFieldDecorator参数的设置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 initValue = item.initialValue || '';let placeholder = item.placeholder;let width =item.width;if(item.type == '时间查询'){const begin_time=<FormItem label={label} key={field} >{getFieldDecorator('begin_time',{initialValue:initialValue,})(<DatePicker showTime={true} placeholder={placeholder} format="YYYY-MM-DD HH:mm:ss"/>)}</FormItem>;formItemList.push(begin_time);const end_time=<FormItem label={label} key={field} >{getFieldDecorator('begin_time',)(<DatePicker showTime={true} placeholder={placeholder} format="YYYY-MM-DD HH:mm:ss"/>)}</FormItem>;formItemList.push(end_time);}if(item.type =='SELECT'){const SELECT = <FormItem label="~" colon={false} key={field} >//colon 不显示label后的:{getFieldDecorator('end_time')(<Select style={{ width:width }}placeholder={placeholder}>{Utils.getOptionList(item.list)}//遍历option 在utils里进行封装</Select>)}</FormItem>;formItemList.push(SELECT)} else if (item.type == 'INPUT'}{const INPUT = <FormItem label={label} key={field} >{getFieldDecorator([field],{initialValue:initialValue,})(<Input type="text" placeholder={placeholder} />)}</FormItem>;formItemList.push(INPUT)}else if (item.type == 'CHECKBOX'}{const SELECT = <FormItem label={label} key={field} >{getFieldDecorator([field],{valuePropName:'checked',//这个属性必须要加上initialValue:initialValue,//checkbox初始值必须为true/false})(<Checkbox> {label}<Checkbox />)}</FormItem>;formItemList.push(CHECKBOX)}})}return formItemList;}render(){return(<Form layout="inline">{this.initFormList()}</Form><FormItem><Button onClick={this.handleFilterSubmit}>查询</Button><Button onClick={this.reset}>重置</Button></FormItem>)}
}
export default Form.create({})(FilterForm);
公共机制封装
打开src/axios/index.js
定义一个专门表单查询的
static requestList(_this,url,params,isMock){//还可以检测是否是mock数据 根据情况变化var data= {params:params//isMock}this.ajax({url:url,data:data,//可以省略为url,data}).then((data)=>{if(data&&data.result){let list = data.result.item_list.map((item,index) => {item.key = index;return item;});_this.setState({list,pagination:Utils.pagination(data,(current) =>{_this.params.page = current;_this.requestList(); //注意 此this非彼this 我们需要修改order index里的requestList 修改如后所示}})}})}order index.js 修改如下 (为了传递this
requestList= ()=>{let _this=this;axios.requestList(this,'/order/list',this.params);
}
表格封装
<Tablebordered //边框columns={columns} //表头dataSource={this.state.list} //数据源 与表头形成一一映射 渲染出表格pagination={this.state.pagination}//分页rowSelection={rowSelection}//控制单选复选onRow={(record,index) =>{return{onClick:()=>{this.onRowClick(record,index);}};}}//跟单选复选配合 点中某一行实现某一行被选中components/ETable/index.js
import Utils from './../../utils/utils'export default class ETable extends React.Component{onRowClick = (record,index)=>{let rowSeletion =this.props.rowSelection;if(rowSelection =='checkbox'){//多选框需要的是idlet 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);//注意 slice不会改变原先数组 只会返回新数组 splice改变原先数组selectedRowKeys.splice(i,1);selectedItem.splice(i,1);}}else{selectedIds=[record.id];selectedRowKeys=[index];selectedItem =[record];this.props.updateSelectedItem(selectedRowKeys,selectedItem,selectedIds)}}else{let selectedRowKeys = [index];let selectedItem =record;//单选不需要[record]数组this.props.updateSelectedItem(selectedRowKeys,selectedItem)}}tableInit =()=>{let selectedRowKeys = this.props.selectedRowKeys;let row_selection =this.props.rowSelection;const rowSelection ={type:'radio',selectedRowKeys,//选中哪一行onChange:this.onSelectChange//跟上个属性配合使用}if(row_selection === false ||row_selection===null){row_selection = false;}else if(row_selection=='checkbox'){row_selection.type = 'checkbox';}else { row_selection ='radio' }this.props return <Tablebordered //边框{...this.props} //把这个源组件的属性传到这里来 即 order index.js的columns datasource paginationrowSelection={row_Selection?rowSelection:null}//控制单选复选onRow={(record,index) =>{//record是一整行数据 index是索引值return{onClick:()=>{if(!row_selection)return; this.onRowClick(record,index);}};}}/>}render(){return(<div>{this.tableInit()}</div>);}
}应用到order/index.js
import ETable from './../../Components/ETable'
把return上面的const selectedRowKeys rowSelection onRowClick都删掉
<div className="content-wrap"><ETableselectedItem ={this.state.selectedItem} //接口统一要id时可以删除selecteditemselectedIds={this.state.selectedIds}updateSelectedItem ={Utils.updateSelectedItem.bind(this)}//把当前页面的作用域传进去 columns={columns}dataSource={this.state.list} //数据源 与表头形成一一映射 渲染出表格pagination={this.state.pagination}//分页selectedRowKeys={this.state.selectedRowKeys}rowSelection={false}/>
</div>Utils.js
updateSelectedItem(selectedRowKeys,selectedItem,selectedIds){//选中的行的key 选中的那一行
//判断单选复选if(selectedIds){this.setState({selectedRowKeys,selectedItem,selectedIds})}else{this.setState({selectedRowKeys,selectedItem})}}
封装了之后可以用一个方法做各种功能
onClick={()=>this.handle('create')}
onClick={()=>this.handle('edit')}
onClick={()=>this.handle('detail')}
onClick={()=>this.handle('delete')}
函数里区别参数即可注意
返回数组时<Radio value={1}>变量形式 而非"1" 字符串形式
返回字符串时才能用"1"要想通过
<Modal footer={this.state.type=='detail'?null:''}>//不可行
解决方案:
<Modal {...footer}>let footer={};
if(this.state.type=='detail'){footer={footer:null}
}
车辆地图
renderMap =(res) =>{let list =res.result.route_list;this.map =new window.BMap.Map('container');//在div是这个名的里面渲染地图let gps1=list[0].split(',');//起点let gps2=list[list.length-1].split(',');//起点let startPoint =new window.BMap.Point(gsp1[0],gsp1[1]);//一个数组里面 左边经度 右边纬度let endPoint =new window.BMap.Point(gsp2[0],gsp2[1]);this.map.centerAndZoom(endPoint,11);//以什么点居中let startPointIcon = new window.BMap.Icoon('url.png',new window.BMap.Size(36,42),{//指定属性imageSize:new window.BMap.Size(36,42)})let endPointIcon = new window.BMap.Icoon('url.png',new window.BMap.Size(36,42),{imageSize:new window.BMap.Size(36,42),anchor:new window.BMap.Size(18,42)})let bikeMarkerStart =new window.BMap.Marker(startPoint,{icon:startPointIcon})
}this.map.addOverlay(bikeMarkerStart);//把点添加到上面同理结束点//绘制车辆行驶路线
let routeList =[];
list.forEach((item)=>{let p=item.split(',');routeList.push(new window.BMap.Point(p[0],p[1]))
})
let polyLine =new window.BMap.Polyline(routeList,{//用折线把routelist里面的点连接起来strokeColor:'#ef4136',strokeWeight:2,strokeOpacity:1
})
this.map.addOverlay(polyLine);
}
//绘制服务区
let servicePointList =[];
let serviceList =res.result.service_list;
serviceList.forEach((item)=>{servicePointList.push(new window.BMap.Point(item.lon,item.lat))
})
let polyServiceLine =new window.BMap.Polyline(servicePointList,{//用折线把routelist里面的点连接起来strokeColor:'#ef4136',strokeWeight:3,strokeOpacity:1
})
this.map.addOverlay(polyServiceLine);//添加图中自行车图标
let bikeList =res.result.bike_list;
let bikeIcon =new window.BMap.Icon('....jpg',new window.BMap.Size(36,42),{...同理})
bikeList.forEach((item)=>{let p=item.split(',');let point = new window.BMap.Point(p[0],p[1]);let bikeMarker =new window.BMap.Marker(point,{icon:bikeIcon})this.map.addOverlay(bikeMarker);
})
图表
ECharts
可以选择把主题下载下来
(主题的源码由AMD-异步模块定义(AMD是RequireJS的)和commonJS
还可以定制主题
–save 可以使该组件保存到package.jsoon里 install即安装了
node版本过高怎么办
降级即可 npm官网搜索n 安装n这个插件
$ n 版本号 即可安装这个版本lts:指的是长期稳定支持的版本echarts是一核心类 做图表肯定要安装的
echarts-for-react 导入之后是组件化的(ES6单页面开发) 不需要像百度地图一样new 一个对象 (多页面的形式) 不需要导入所有的图标 即import echarts from 'echarts'
按需加载:
import echarts from 'echars/lib/echarts'主题导入://注意先把下载好的主题文件放入定义好的src/echarts/包里
import echartTheme from './../echartTheme'导入柱形图://如何看在哪个包里呢 搜npm里echars 或github
import 'echars/lib/chart/bar'
import 'echars/lib/component/tooltip' //图表上的信息框
import 'echars/lib/component/title'
import 'echars/lib/component/legend'
import 'echars/lib/component/markPoint'import ReactEcharts from 'echarts-for-react'componentWillMount(){//注册主题echarts.registerTheme('自定义主题名',echartTheme);//导入的这个主题
}getOption(){let option ={title:{text:'用户骑行订单'},tooltip:{trigger:'axis'},xAxis:{//x轴数据data:['周一','周二',....] //可以直接从服务端提取数据即可},yAxis:{type:'value' //把数据自动计算 列出来即可},series:[//定义数据量 数据源 展示{name:'订单量',type:'bar',data:[1000,2000,1500,...],}]}return option;
}getOption2(){let option ={title:{text:'用户骑行订单'},legend:{data:['OFO','摩拜‘,’小蓝'] //渲染出来一个关于同一x轴不同分类 点击一方可进行不展示操作},tooltip:{trigger:'axis'},xAxis:{//x轴数据data:['周一','周二',....] //可以直接从服务端提取数据即可},yAxis:{type:'value' //把数据自动计算 列出来即可},series:[//定义数据量 展示{name:'OFO',type:'bar',data:[1000,2000,3500,...],},{name:'摩拜',type:'bar',data:[2000,3000,4500,...],},{name:'小蓝',type:'bar',data:[5000,5200,5500,...],}]}return option;
}<Card title="柱形图1"> <ReactEcharts optioon={this.getOption() } theme="自定义主题名” /> //跟上面保持一致 先注册再使用</Card><Card title="柱形图2"> <ReactEcharts optioon={this.getOption2() } theme="自定义主题名” /> //跟上面保持一致 先注册再使用</Card>饼图
getOption= () => {let option ={title:{text:'用户骑行订单',x:'center'},tooltip:{trigger:'item',//指数据项formatter:'{a}<br />{b}:{c}({d}%)'//格式化:系列名 回车 数据名 value 比例%},legend:{right:10,top:20,botton:20,orient:'vertical',//垂直方向居中data:['OFO','摩拜‘,’小蓝'] //渲染出来一个关于同一x轴不同分类 点击一方可进行不展示操作},series:[//定义数据量 展示{name:'订单量',//系列名type:'pie',radius:['50%,‘30%’],//有这一项就是环形图半径 分别控制内外环 没有就是大饼图center:['50%','60%'],//整个图往左往下移动data:[{value:1000,name:'周一'//数据名},{...},{...}],}]}return option;
}还有一种根据数据量定义半径大小的饼图(南丁格尔图
首先要对数据进行排序
getOption= () => {let option ={title:{text:'用户骑行订单',x:'center'},tooltip:{trigger:'item',//指数据项formatter:'{a}<br />{b}:{c}({d}%)'//格式化:系列名 回车 数据名 value 比例%},legend:{right:10,top:20,botton:20,orient:'vertical',//垂直方向居中data:['OFO','摩拜‘,’小蓝'] //渲染出来一个关于同一x轴不同分类 点击一方可进行不展示操作},series:[//定义数据量 展示{name:'订单量',//系列名type:'pie',data:[{value:1000,name:'周一'//数据名},{...},{...}].sort((a,b)=>{return a.value -b.value;}),roseType:'radius'//显示风格为南丁格尔图//还可以加动画animationType:'scale',//鼠标移上去松开会变大变小animationEasing:'elasticOut',animationDelay:function (idx){return Math.random() *200;}}]}return option;
}折线
import 'echars/lib/chart/line'
getOption= () => {let option ={title:{text:'用户骑行订单',},tooltip:{trigger:'axis'//指数据项},xAxis:{type:'category',//默认值boundaryGap:false,//控制面积x轴上开始的起点是0 还是第一个数据点data:['周一',....]},yAxis:{type:'value'},series:[//定义数据量 展示{type:'line',data:[1500,2500,1000,...],areaStyle:{}//填充面积}]}return option;
}import 'echars/lib/chart/line'
getOption= () => {let option ={title:{text:'用户骑行订单',},tooltip:{trigger:'axis'//指数据项},legend:{data:['ofo订单量','mobike订单量']},xAxis:{data:['周一',....]},yAxis:{type:'value'},series:[//定义数据量 展示{name:'ofo订单量',//系列名type:'line',data:[1500,2500,1000,...]}},{name:'mobike订单量',//系列名type:'line',data:[1000,2000,1500,...]}}]}return option;
}富文本功能 满足文字各种样子的需求
npm官网里有react-draft-wysiwyg插件
还需要一个转化成HTML内容的插件 draftjs-to-html
都需要终端进行安装
import {Editor } from 'react-draft-wysiwyg';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import draftToHtml from 'draftjs-to-html';state={showRichText:false //默认不显示
editorState:''
}onEditorChange=(contentState)=>{this.setState({contentState//内容状态});
}onEditorStateChange =( editorState) =>{this.setState({editorState,//编辑器状态});
}handleClearContent =()=>{this.setState({editorState:' ' ,});
}handleGetText =() =>{this.setState({showRichText:true});
}render(){const { editorState } =this.state;return(<div><Card><Button type="primary" onClick={this.handleClearContent}>清空内容</Button ><Button type="primary" onClick={this.handleGetText}>获取html文本</Button></Card><Card><EditoreditorState={editorState} //接收到状态值onContentStateChange={this.onEditorChange}onEditorStateChange={this.onEditorStateChange} //状态值变化之后/></Card></div><Modaltitle="。。。" visible={this.state.showRichText}onCancel={ ()=>{this.setState({showRichText:false}) }footer={null}}>{draftjs(this.state.contentState)}//转化成对应的html文本</Modal>)
}
项目实战二:共享单车后台2相关推荐
- Taro多端开发实现原理与项目实战(二)
Taro多端开发实现原理与项目实战(二) 多端电商平台项目概述及开发准备 学习了前面的基础知识和进阶后是否跃跃欲试?我们准备了一个电商平台的项目来和大家一起实践使用 Taro 开发电商平台. 项目概述 ...
- flutter 项目实战二 网络请求
本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用. 逛丢官方网址:https://guangdiu.com/ flutter windows开发环境设置 flutter 项目实战 ...
- c语言期中项目实战二—简易扫雷,思路分析加代码详细注释
c语言期中项目实战二-简易扫雷,思路分析+代码详细注释 游戏介绍 项目步骤 模块化编程 设置菜单 设置棋盘 打印棋盘 布置雷 排查雷 总结及总代码和详细注释 游戏介绍 扫雷这个经典游戏,直到现在仍有很 ...
- Python业务分析实战|共享单车数据挖掘
本文详细介绍了共享单车数据挖掘,包括数据分析和模型开发.它包含以下步骤: 共享单车数据挖掘 数据集简介 关于共享单车数据集 自行车共享系统是传统自行车租赁的新一代,从注册会员.租赁到归还的整个过程都是 ...
- 机器学习项目一:共享单车
共享单车项目 import numpy as np import pandas as pd from sklearn.decomposition import PCA,TruncatedSVD fro ...
- Vue项目实战之电商后台管理系统(一) 用户登录模块
目录 一.项目概述 二.项目初始化 2.1 前端项目初始化步骤 2.2 后台项目的环境安装配置 三.用户登录/登出功能实现 3.1 登录功能概述 3.1.1 登录状态保持 3.1.2 登录逻辑: 3. ...
- 前端开发Vue项目实战:电商后台管理系统(二)-- 登录退出功能 --主页界面
目录 1. 登录/退出功能 1.1 登录概述 1.2 token 原理分析 1.3 登录功能实现 1.3.1 Git 创建分支 1.3.2 渲染Login组件并实现路由重定向 1.3.3 设置背景颜色 ...
- Vue项目实战之电商后台管理系统(二) 主页模块
前言 目录 前言 一.主页布局 1.1 整体布局 1.2 头部区域布局 1.3 左侧菜单布局 1.3.1 静态布局 1.3.2 通过axios请求拦截器来进行权限验证 1.3.3 通过axios获取左 ...
- 微信小程序项目实战+JAVA SSM框架后台管理系统
毕业设计做的是一个阅读微信小程序+后台管理系统 ,最后被评为优秀毕业设计,在此将项目源码及设计思路进行分享(文末含源码下载地址). 效果图如下: 一.系统开发环境 (1)Windows10操作系统 ( ...
最新文章
- 计算机试题高考作文阅卷组对考生,2017年广东高考评卷收尾
- linux squid日志滚动,linux squid 日志
- 低成本、高性能创客开发板——PYB Nano
- 国防科大提出基于可变形三维卷积(D3Dnet)的视频超分辨,代码已开源
- python 格式化工具_推荐一个小而美的 Python 格式化工具
- 用牛顿法求方程的根的c语言编程,用牛顿迭代法和二分法求方程的根【C语言】...
- sql server计算日期到当前日期天数_Excel必学的7个计算日期间隔差的技巧
- 量化指标公式源码_量化庄建仓(副图指标源码)下载 通达信源码
- 手机图案密码(3*3点阵)开锁次数 C++
- 机器视觉之镜头景深概念与计算
- stata输出相关系数表到word
- BZOJ 2339 【HNOI2011】 卡农
- 计算机一级表格技巧,计算机一级考试MS Office应试技巧指导
- Zabbix实现企业微信报警
- Android下监听返回键、home键、任务键
- 在阿里云从0开始部署vue+springboot项目
- PS2017使用快速选择工具的时候因内存不足提示“要求96和8之间的整数,已插入最接近的数值”问题解决方案
- 如何用继电器实现逻辑门(与或非门)- 编码隐匿在计算机软硬件背后的语言读后感
- 考考你的眼力 又一张神奇的图片
- 向程序发送命令时出现错误