今天听雷鹏飞大佬讲了一个 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下面把