项目实战二:共享单车后台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会重新渲染dom
	dataSource2:[]//dataSource2如果不存入state render不执行
}
params={//只改变页码不需要改变dom结构
	page:1 //接口调用需要 而不是页面需要
}
componentDidMount(){
	const dataSource =[//数据源
		{
			id:'0',
			userName:'Jack'
			sex:'1',
			...
		}
	]
	data.map((item,index)=>{
		item.key = index;
	}) //datasource在表格中应有独一无二的key
	this.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){//或者state
				let config ={//太多了的话也可以专门定义一个字典文件.js
					'1':'咸鱼',
					....
				}
				return config[abc];//或者state
			}
			还有一种写法:
			render(abc){//或者state
				return {
					'1':'咸鱼',
					'2':'风华浪子',
					...
				}[abc];//或者state
			}
		}
		...
	]
	const { selectedRowKeys } =this.state;//等于
	const selectedRowKeys =this.state.selectedRowKeys ;
	const rowSelection = {
		type:'radio',//复选为checkbox
		selectedRowKeys//存选中某一项的key
		//也可以加onchange:意为选中下一个框时发生了改变启动事件
	}
	const rowSelection = {
		type:'checkbox',//复选为checkbox
		selectedRowKeys,//存选中某一项的key
		onChange:(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就show
		loading = 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就show
				loading = document.getElementById('ajaxLoading'); //loading代码在全局html文件里 
		//直接用这种方式获取
				loading.style.display = 'none';//关闭掉
			}
			if(response.status == '200'){//HTTP状态请求码
				let res= response.data;
				if(res.code == '0') {//业务代码成功定义为0
					resolve(res);//把res.data抛出去 我们可以接收到
				}else{
					Modal.info({ //错误信息
						title:"提示",
						content:res.msg
					})
				}
			}
			else {reject(response.data);}//把它打印出来并报错
		})
	});
	
}

封装了之后我们可以修改上述的request 即
request =() =>{
	let _this =this;//怕作用域出问题 提前提取this
	axios.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,//这一列宽度为80
			dataIndex:'id'//数据项中所对应的key
		},
		{
			title:'用户名',//列名
			dataIndex:'userName'
		},
		{
			title:'性别',//列名
			dataIndex:'sex',
			render(sex){
				return sex==1 ? '男':'女'
			}
		},
		{
			title:'状态',
			dataIndex:'state',
			render(abc){//或者state
				let config ={//太多了的话也可以专门定义一个字典文件.js
					'1':'咸鱼',
					....
				}
				return config[abc];//或者state
			}
		}
		...
	]
	const columns2 =[//定义表头
		{
			title:'id',//列名
			key:'id',
			width:80,//这一列宽度为80
			fixed:'left', //该列左侧固定不动
			dataIndex:'id'//数据项中所对应的key
		},
		{
			title:'用户名',//列名
			dataIndex:'userName'
		},
		{
			title:'性别',//列名
			dataIndex:'sex',
			render(sex){
				return sex==1 ? '男':'女'
			}
		},
		{
			title:'状态',
			dataIndex:'state',
			render(abc){//或者state
				let config ={//太多了的话也可以专门定义一个字典文件.js
					'1':'咸鱼',
					....
				}
				return config[abc];//或者state
			}
		}
		...
	]
	const columns3 =[//定义表头
		{
			title:'id',//列名
			key:'id',
			width:80,//这一列宽度为80
			dataIndex:'id'//数据项中所对应的key
		},
		{
			title:'用户名',//列名
			dataIndex:'userName'
		},
		{
			title:'年龄',//列名
			key:'age',
			dataIndex:'age',
			width:80,
			sorter:(a,b)=>{
				return a.age -b.age; //小于0则a
			},
			sortOrder:this.state.sortOrder
		},
		{
			title:'性别',//列名
			dataIndex:'sex',
			render(sex){
				return sex==1 ? '男':'女'
			}
		},
		{
			title:'状态',
			dataIndex:'state',
			render(abc){//或者state
				let config ={//太多了的话也可以专门定义一个字典文件.js
					'1':'咸鱼',
					....
				}
				return config[abc];//或者state
			}
		}
		...
	]
	const columns4 =[//定义表头
		{
			title:'id',//列名
			列宽度为80
			dataIndex:'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>
			<Modal
				title="开通城市"
				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)=>{//写法1
		let 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">
				<Table
					bordered
					columns={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')(
						<Select
							style={{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下面把

你可能感兴趣的:(项目实战二:共享单车后台2)