本文基于React共享单车后台管理系统的开发进行学习记录,内含React生态系统的总体介绍及相关内容整理,方便本身查阅,也但愿对小伙伴有所帮助 css
Facebook开源的一个JavaScript库html
React结合生态构成的一个MV* 框架node
Vue生态:react
Vue+Vue-Router+Vuex+Axios+Babel+Webpack
复制代码
React生态:webpack
React+React-Router-Redux+Axios+Babel+Webpack
复制代码
getDefaultPropsios
经过这个方法,来初始化一个Props属性,Props就来自于父组件git
getInitialStatees6
初始化state,能够直接在constructor中定义this.stategithub
componentwillMountweb
组件加载时只调用,之后组件更新不调用,整个生命周期只调用一次,此时能够修改state
render
是一个React组件必须定义的生命周期,用来渲染dom,是使用最多,最重要的放方法
componetDidMount
组件渲染以后调用,只调用一次
componentwillReceiveProps
组件加载时不调用,组件接受新的props时调用
shouldComponentUpdate
组件接收新的props或者state时调用
componentwillUpdate
在组件接收新的props或者state但尚未render时被调用,在初始化时不会被调用
componentDidUpdate
组件完成更新后当即调用,在初始化时不会被调用
componentWillUnmount
在组件从DOM中移除以前马上被调用
单项数据流:
从父组件流向子组件,兄弟组件没法共享数据
state:
React中的状态,是只读对象,不可直接修改
Reducer:
基本函数,用于对state的业务处理
Action:
普通对象,用于描述事件行为,改变state
yarn add redux --save
yarn add react-redux --save
复制代码
建立Action模块
建立Reducer模块
建立Store模块
经过connect方法将React组件和Redux链接起来
添加Provider做为项目的根组件,用于数据的存储
在Chrome中安装Redux Devtools扩展
yarn add redux-devtools-extension
复制代码
npm install -g create-react-app //安装这个脚手架
create-react-app may-app //初始化一个项目
cd my-app //切换
npm start //启动进入
复制代码
Yarn是新一代的包管理工具
yarn init //初始化一个项目
yarn add //安装一个包
yarn remove //删除一个包
yarn / yarn install //安装依赖包
复制代码
课程地址:coding.imooc.com/class/236.h…
安装脚手架
npm install -g create-react-app
复制代码
查看版本号,看是否安装成功
create-react-app --version
复制代码
初始化这个项目
create-react-app imoocmanager
复制代码
切换
cd imoocmanager
复制代码
启动进入
npm start
复制代码
安装yarn
npm install -g yarn
复制代码
查看版本号,看是否安装好
yarn --version
复制代码
注:
这个项目没有webpack配置,这就是脚手架的特点,脚手架把webpack的配置所有给它封装起来了,因此只须要关心src的源码部分
引入antd(安装支付宝的UI框架)
yarn add antd --save
复制代码
加入其它依赖
yarn add axios --save //Axios 是一个基于 promise 的 HTTP 库,能够用在浏览器和 node.js 中
yarn add jsonp --save //跨域请求
yarn add less --save //(2.7.3的版本),超过此版本,antd按需加载报错.bezierEasingMixin();
yarn add less-loader --save //antd 用的是less预处理,因此也添加less,保持一致。
yarn add moment --save //日期处理
yarn add babel --svae //转换react jsx语法和转换es6 语法后才能兼容智障浏览器
yarn add babel-polyfill --save
yarn add babel-plugin-import --save //不添加的话 IE会报错TypeError: 对象不支持“startsWith”属性或方法
复制代码
定义components包,分别加入Footer、Header、NavLeft包,内部加入index.js文件,导入时能够导入到包的级别,系统会自动寻找包下的index.js文件
定义style包,内部加入common.less文件定义全局样式
例子使用:
width:clac(100%-50px) //表示宽度属性是整个布局的100%减去50px的长度
复制代码
//css中
div{...}
div a{...}
//less中
div{
...
a:{
...
}
}
复制代码
@colorA:'red'
div{
color:@colorA
a:{
color:black
}
}
复制代码
admin.js
import React,{Component} from 'react'
import { Row,Col } from 'antd';
import Header from './components/Headers/index'
import Footer from './components/Footer/index'
import NavLeft from './components/NavLeft/index'
import './style/common.less'
// import Row from './components/NavLeft/index'
export default class Admin extends Component{
render (){
return(
<Row className="container">
<Col span="5" className="nav-left" >
<NavLeft />
</Col>
<Col span="19" className="main">
<Header />
<Row className="content">
{this.props.children}
</Row>
<Footer />
</Col>
</Row>
)
}
}
复制代码
src/style/commmon.less
.container{
display: flex;
.nav-left{
background-color:#001529;
color: #ffffff;
height: calc(100vh);
}
.main{
height: calc(100vh);
background-color: @colorL;
overflow: auto;
}
.content{
position: relative;
padding: 20px;
}
}
复制代码
src/components/NavLeft/index.js
import React,{Component} from 'react'
import { Menu, Icon } from 'antd';
import MenuConfig from './../../config/menuConfig'
import './index.less'
const SubMenu = Menu.SubMenu;
export default class NavLeft extends Component{
componentWillMount(){
const menuTreeNode = this.renderMenu(MenuConfig);
this.setState ({
menuTreeNode
})
}
//菜单渲染
renderMenu =(data)=>{
return data.map((item)=>{
if(item.children){
return (
<SubMenu title={item.title} key={item.key}>
{this.renderMenu(item.children)}
</SubMenu>
)
}
return <Menu.Item title={item.title} key={item.key}>{item.title}</Menu.Item>
})
}
render (){
return(
<div>
<div className="logo">
<img src="/assets/logo-ant.svg" alt=""/>
<h1>BikeSharing</h1>
</div>
<Menu theme="dark">
{ this.state.menuTreeNode }
</Menu>
</div>
)
}
}
复制代码
在public文件夹下添加assets文件夹,放置logo-ant.svg图片
注意:public文件是build文件是build以后存储内容的文件,一般内部放置静态资源,public下的文件内容在访问时全是经过根目录直接访问文件地址
编写Navleft组件内容/src/Navleft/index.js,使用antd中的Menu和Icon组件,Menu组件的theme属性能够设置菜单样式。对应的SubMenu组件须要设置title和key属性值,Menu.Item组件须要设置title和key属性值和组件内容
src/NavLeft/index.less
编写Navleft组件样式
.logo{
line-height: 90px;
padding-left: 20px;
background-color: #002140;
img{
height: 35px;
}
h1{
color: #ffffff;
font-size: 20px;
display: inline-block;
vertical-align: middle;
margin: 0 0 0 10px;
}
}
复制代码
src/config/menuConfig.js
const menuList = [
{
title:'首页',
key:'/admin/home'
},
{
title:'UI',
key:'/admin/ui',
children:[
{
title:'按钮',
key:'/admin/ui/buttons',
},
{
title:'弹框',
key:'/admin/ui/modals',
},
{
title:'Loading',
key:'/admin/ui/loadings',
},
{
title:'通知提醒',
key:'/admin/ui/notification',
},
{
title:'全局Message',
key:'/admin/ui/messages',
},
{
title:'Tab页签',
key:'/admin/ui/tabs',
},
{
title:'图片画廊',
key:'/admin/ui/gallery',
},
{
title:'轮播图',
key:'/admin/ui/carousel',
}
]
},
{
title:'表单',
key:'/admin/form',
children:[
{
title:'登陆',
key:'/admin/form/login',
},
{
title:'注册',
key:'/admin/form/reg',
}
]
},
{
title:'表格',
key:'/admin/table',
children:[
{
title:'基础表格',
key:'/admin/table/basic',
},
{
title:'高级表格',
key:'/admin/table/high',
}
]
},
{
title:'富文本',
key:'/admin/rich'
},
{
title:'城市管理',
key:'/admin/city'
},
{
title:'订单管理',
key:'/admin/order',
btnList:[
{
title:'订单详情',
key:'detail'
},
{
title:'结束订单',
key:'finish'
}
]
},
{
title:'员工管理',
key:'/admin/user'
},
{
title:'车辆地图',
key:'/admin/bikeMap'
},
{
title:'图标',
key:'/admin/charts',
children:[
{
title:'柱形图',
key:'/admin/charts/bar'
},
{
title:'饼图',
key:'/admin/charts/pie'
},
{
title:'折线图',
key:'/admin/charts/line'
},
]
},
{
title:'权限设置',
key:'/admin/permission'
},
];
export default menuList;
复制代码
在config下编写manuConfig.js返回导航栏内容title和key
效果以下:
利用jsonp能够解决跨域问题(所谓跨域就是跨域名,跨端口,跨协议)
web页面上调用js文件时则不受跨域影响(且凡有src属性的标签都具备跨域能力,如<\script> <\img> <\iframe>等)
Promise最大好处
将回调函数的异步调用变成链式调用。函数返回的promise对象参数接受一个函数,参数1为成功resolve回调函数,参数2是失败的reject回调函数,函数体中返回对应函数调用。调用该返回Promise对象的函数的then方法参数为resolve函数传入的属性值为参数的函数,在方法体内写入回调成功返回的内容
src/components/Header/index.js
利用setInterval函数实时刷新时间信息,并调用axios包中的jsonp方法发送请求并根据promise进行回调
import React,{Component} from 'react'
import { Row,Col } from 'antd';
import './index.less'
import Util from '../../utils/utils'
import axios from '../../axios/index'
export default class Header extends Component{
componentWillMount(){
this.setState({
userName:'Dream_Lee'
})
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)=>{
// debugger;
if(res.status == 'success'){
let data = res.results[0].weather_data[0];
this.setState({
dayPictureUrl:data.dayPictureUrl,
weather:data.weather
})
}
})
}
render (){
return(
<div className="header" >
<Row className="header-top">
<Col span="24" >
<span >欢迎</span>
<span className="welcome">{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>
)
}
}
复制代码
src/components/Header/index.less
.header{
.header-top{
height: 60px;
line-height: 60px;
padding: 0 20px;
text-align: right;
.welcome{
margin-left: 5px;
}
a{
margin-left: 20px;
}
}
.breadcrumb{
height: 40px;
line-height: 40px;
padding: 0 20px;
border-top: 1px solid #f9c700;
.breadcrumb-title{
text-align: center;
}
.weather{
text-align: right;
.date{
margin-right: 10px;
vertical-align: middle;
}
.weather-detail{
margin-left: 5px;
vertical-align: middle;
}
.weather-img{
img{
height: 15px;
}
}
}
}
}
复制代码
src/untils/untils.js
import React,{Component} from 'react'
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()+' ';
}
}
复制代码
src/axios/index.js利用Jsonp
.header{
.header-top{
height: 60px;
line-height: 60px;
padding: 0 20px;
text-align: right;
.welcome{
margin-left: 5px;
}
a{
margin-left: 20px;
}
}
.breadcrumb{
height: 40px;
line-height: 40px;
padding: 0 20px;
border-top: 1px solid #f9c700;
.breadcrumb-title{
text-align: center;
}
.weather{
text-align: right;
.date{
margin-right: 10px;
vertical-align: middle;
}
.weather-detail{
margin-left: 5px;
vertical-align: middle;
}
.weather-img{
img{
height: 15px;
}
}
}
}
}
复制代码
效果以下
src/components/Footer/index.js
import React from 'react'
import './index.less'
export default class Footer extends React.Component{
render(){
return(
<div className="footer">
版权全部:慕课网&河畔一角(推荐使用谷歌浏览器,能够得到更佳操做页面体验) 技术支持:河畔一角
</div>
);
}
}
复制代码
src/components/Footer/index.less
@import './../../style/default';
.footer{
height: 100px;
padding: 40px 0;
text-align: center;
color:@colorJ;
;
}
复制代码
效果以下:
4.0版本已不须要路由配置,一切皆组件
react-router:基础路由包
提供了一些router的核心api,包括Router,Route,Switch等
react-router-dom:基于浏览器端的路由(包含react-router)
提供了BrowserRouter,HashRouter,Route,Link,NavLink
安装:npm install react-router-dom --save 或 yarn add react-router-dom
react-router-dom核心用法
HashRouter和BrowserRouter
Route:path、exact、component、render
注:exact属性表明精准匹配,必须path彻底匹配才能够加载对应组件
NavLink应用于菜单里做为菜单导航、Link超连接导航
eg1.
import {Link} from 'react-router-dom';
const Header=()=>{
<header>
<nav>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/three'>Three</Link></li>
</nav>
</header>
}
复制代码
<Link to={{pathname:'/three/7'}}>Three #7</Link>
复制代码
{pathname:'/',search:'',hash:'',key:'abc123',state:{}}
复制代码
<Switch>
<Route path='/admin/ui/buttons' component={Buttons}/>
<Route path='/admin/ui/models' component={Models}/>
<Route path='/admin/ui/loading' component={Loading}/>
</Switch>
复制代码
路由重定向:<Redirect to="/admin/home">
复制代码
4.0基本路由功能DEMO实现-混合组件化【将Route和Link放在同一页面】
HashRouter将Link和Router进行包裹,其内部必须只能有一个子节点
经过Link组件的to属性设置路由地址;经过Route组件的path属性匹配路由地址,从而渲染对应component中的组件【注意:Route添加exact属性能够作到路径的精准匹配,不然/about既能够匹配/也能够匹配/about等路由地址】
实战代码
Home.js
用于显示整个页面内容
import React from 'react';
import {HashRouter,Route,Link,Switch} from 'react-router-dom'
import Main from './Main';
import About from './About';
import Topics from './Topics';
class Home extends React.Component{
render(){
return(
<HashRouter>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr></hr>
<Route path="/" exact={true} component={Main}></Route>
<Route path="/about" component={About}></Route>
<Route path="/topics" component={Topics}></Route>
</div>
</HashRouter>
);
}
}
export default Home;
复制代码
Main.js组件
import React,{Component} from 'react';
class Main extends Component{
render(){
return(
<div>
this is Main.
</div>
);
}
}
export default Main;
复制代码
Topic.js组件
import React,{Component} from 'react';
class Topics extends Component{
render(){
return(
<div>
this is Topics.
</div>
);
}
}
export default Topics;
复制代码
About.js组件
import React,{Component} from 'react';
class About extends Component{
render(){
return(
<div>
this is About.
</div>
);
}
}
export default About;
复制代码
4.0基本路由功能DEMO实现-配置化【将Route路由提取到一个单独的JS文件中】
配置化实现路由功能
建立Home.js内部写上ul->li->Link导航组件,并在想要显示对应路由内容的区域写上{this.props.children}即会加载调用Home.js组建时内部传递的信息
建立router.js,最外层用HashRouter包裹,第二级用Home组件包裹,内部写对应路由Route(path路由路径匹配、及component属性渲染的组件)
执行流程,index.js渲染Router组件时,因为最外层是HashRouter,内部是Home组件故加载Home组件内容,并经过{this.props.children}获得在调用Home组件时内部的信息
嵌套路由
如想实如今Main组件中的嵌套路由,须要在Main组件中添加{this.props.children}从而渲染对应的内部信息,还须要添加Link组件以进行跳转
以后在router.js中对应调用该组件的Route组件中,删除component属性,添加render属性进行页面渲染,render属性内应是一个函数,返回Main组件(内部带有Route属性以进行路由渲染)
注意点:
调用Main组件的Route不能添加exact属性,由于若是添加exact属性,当点击内部组件路由到对应url时因为与外部Route的path不是彻底匹配,故将不会显示
调用Main组件的Route内部render函数若是添加()=>{}则须要在函数体内写return,由于{}表示函数体,内部的函数将被执行,返回组件须要写return;若是不添加大括号则直接写返回的组件便可,ES6箭头函数默认箭头后面的内容是return的
调用Main组件的Route内部Main里的Route组件的path须要包含外部Route的path做为一级路由,如外部是/main内部须要是/main/xxx,若是不之外部Route做为一级路由则不会走外部的Route到内部Route内容
实战代码
/src/pages/router-demo/router2/router.js
import React from 'react';
import {HashRouter as Router,Route,Link}from 'react-router-dom';
import Main from './../router1/Main';
import About from './../router1/About';
import Topics from './../router1/Topics';
import Home from './Home';
class IRoute extends React.Component{
render(){
return(
<Router>
<Home>
<Route path="/main" render={()=>{
return(
<Main>
<Route path="/main/a" component={About}></Route>
</Main>
);}}></Route>
<Route path="/about" component={About}></Route>
<Route path="/topics" component={Topics}></Route>
</Home>
</Router>
);
}
}
export default IRoute;
复制代码
/src/pages/router-demo/router2/Home.js
import React from 'react';
import {Link} from 'react-router-dom'
class Home extends React.Component{
render(){
return(
<div>
<ul>
<li>
<Link to="/main">Home1</Link>
</li>
<li>
<Link to="/about">About1</Link>
</li>
<li>
<Link to="/topics">Topics1</Link>
</li>
</ul>
<hr></hr>
{this.props.children}
</div>
);
}
}
export default Home;
复制代码
/src/pages/router-demo/router2/Main.js
import React,{Component} from 'react';
import {Link} from 'react-router-dom';
class Main extends Component{
render(){
return(
<div>
this is Main.
<Link to="/main/a">To start a</Link>
<hr></hr>
{this.props.children}
</div>
);
}
}
export default Main;
复制代码
在main.js中设置跳转的路由连接
import React,{Component} from 'react';
import {Link} from 'react-router-dom';
class Main extends Component{
render(){
return(
<div>
this is Main.<br/>
<Link to="/main/test-id">嵌套路由1</Link><br/>
<Link to="/main/456">嵌套路由2</Link>
<hr></hr>
{this.props.children}
</div>
);
}
}
export default Main;
复制代码
在router.js中添加动态路由即path:"/main/:value"用冒号定义的路由内容
import React from 'react';
import {HashRouter as Router,Route,Link,Switch}from 'react-router-dom';
import Main from './Main';
import About from './../router1/About';
import Topics from './../router1/Topics';
import Home from './Home';
import Info from './Info';
import NoMatch from './NoMatch';
class IRoute extends React.Component{
render(){
return(
<Router>
<Home>
<Switch>
<Route path="/main" render={()=>{
return(
<Main>
<Route path="/main/:value" component={Info}></Route>
</Main>
);}}></Route>
<Route path="/about" component={About}></Route>
<Route path="/topics" component={Topics}></Route>
<Route component={NoMatch}></Route>
</Switch>
</Home>
</Router>
);
}
}
export default IRoute;
复制代码
在Info.js中获取定义的动态路由内容信息,经过{this.props.match.params.路由的名称}
import React,{Component} from 'react';
class Info extends Component{
render(){
return(
<div>
这里是测试动态路由功能
动态路由的值是{this.props.match.params.value}
</div>
);
}
}
export default Info;
复制代码
添加默认路由
添加react-router-dom的Switch组件包裹Route组件用于设置自上自下只匹配一个路由
添加没有path属性的Route组件放置Switch组件内部的最后位置,做为默认路由
NoMath.js
import React from 'react';
class NoMatch extends React.Component{
render(){
return(
<div>
404 Not Found
</div>
);
}
}
export default NoMatch;
复制代码
因为用户访问项目时输入url须要有对应的输出,而做为整个文件输出时,一共有三种状况:登陆、详情页、首页。故须要编写项目的入口文件router.js并在index.js中引入
router文件中定义使用路由的方式为HashRouter
因为咱们要访问完整路由时有登陆页面、详情页面和首页,router文件须要定义根组件App,App.js内部什么都没有只有{this.props.children},主要用来存放子组件(即上述三个页面)。【若是没有用App进行包裹,即没有地方设置{this.props.children}显示路由的页面内容,即上述三个页面无法显示】
总结:想利用Route显示组件信息,则必须调用{this.props.children}显示其页面的组件
有三个页面就须要有三个路由path分别为/login渲染登陆组件、/admin渲染首页(其中里用render函数返回子路由Admin组件,并定义嵌套路由/admin/ui/buttons显示组件按钮;无path显示404页面,外层用Switch包裹保证只显示其中第一个匹配的Route)、和/order/detail渲染详情页
/src/router.js
import React,{Component} from 'react';
import {HashRouter,Route,Switch} from 'react-router-dom';
import App from './App';
import Login from './pages/login';
import Admin from './admin';
import Buttons from './pages/ui/buttons';
import NoMatch from './pages/nomatch';
class IRouter extends Component{
render(){
return(
<HashRouter>
<App>
<Route path="/login" component={Login}></Route>
<Route path="/admin" render={()=>{
return(
<Admin>
<Switch>
<Route path="/admin/ui/buttons" component={Buttons}></Route>
<Route component={NoMatch}></Route>
</Switch>
</Admin>
);
}}></Route>
<Route path="/order/detail" component={Login}></Route>
</App>
</HashRouter>
);
}
}
export default IRouter;
复制代码
将Admin组件中的content部分使用{this.props.children}显示在router.js中的Route获得的页面
/src/admin.js
import React from 'react';
import { Row, Col } from 'antd';
import Header from './components/Header';
import Footer from './components/Footer';
import NavLeft from './components/NavLeft';
import Home from './pages/home';
import './style/common.less'
class Admin extends React.Component{
render(){
return(
<Row className="container">
<Col span={6} className="nav-left">
<NavLeft/>
</Col>
<Col span={18} className="main">
<Header/>
<Row className="content">
{/* <Home></Home> */}
{this.props.children}
</Row>
<Footer/>
</Col>
</Row>
);
}
}
export default Admin;
复制代码
有Route就必定要有Link指定路由地址,咱们在首页中经过左侧导航栏进行跳转,故须要在NavLeft组件中利用react-router-dom的NavLink设置路由地址,NavLink组件显示的内容为{item.title},to跳转的地址为{item.key}
/src/components/Navleft/index.js
import React from 'react';
import {Menu,Icon} from 'antd';
import MenuConfig from './../../config/menuConfig';
import './index.less';
import MenuItem from 'antd/lib/menu/MenuItem';
import {NavLink} from 'react-router-dom';
const SubMenu = Menu.SubMenu;
class NavLeft extends React.Component{
componentWillMount(){
const menuTreeNode = this.renderMenu(MenuConfig);
this.setState({
menuTreeNode
})
}
//菜单渲染
renderMenu=(data)=>{
return data.map((item,index)=>{
if(item.children){
return(
<SubMenu title={item.title} key={item.key}>
{this.renderMenu(item.children)}
</SubMenu>
)
}
return <Menu.Item title={item.title} key={item.key}>
<NavLink to={item.key}>{item.title}</NavLink>
</Menu.Item>
})
}
render(){
return(
<div>
<div className="logo">
<img src="/assets/logo-ant.svg" alt=""></img>
<h1>Imooc MS</h1>
</div>
<Menu theme="dark">
{this.state.menuTreeNode}
</Menu>
</div>
);
}
}
export default NavLeft;
复制代码
在router.js中能够看到若是匹配的是/admin/ui/buttons则将Button组件渲染到Admin组件的content中;如没有写对应的路由匹配则将404页面渲染到Admin组件的content中,对应代码以下
/src/pages/ui/buttons.js
import React,{Component} from 'react';
class Buttons extends Component{
render(){
return(
<div>
This is Buttons Page.
</div>
)
}
}
export default Buttons;
//[/src/pages/nomatch/index.js]
import React from 'react';
class NoMatch extends React.Component{
render(){
return(
<div style={{textAlign:'center',fontSize:'24'}}>
404 Not Found!!!
</div>
);
}
}
export default NoMatch;
复制代码
import {Card} from 'antd'
复制代码
<Card title="基础组件"></Card>
复制代码
import {Button} from 'antd'
复制代码
type属性值
primary表示主按钮
不写type表示默认样式按钮
dashed表示虚线按钮
danger表示危险按钮
disable属性值表示禁用按钮
icon属性值表示按钮图标样式
plus表示加号
danger表示危险按钮
delete表示删除
search表示搜索
download表示下载
shape属性表示按钮形状
circle表示圆形
loading属性为{true}表示加载中(此时按钮不能点击)
按钮组为Button.Group组件,用于表示包含的Button组件是一个组
size属性表示组件大小
small小按钮组件
default默认大小按钮组件
large大按钮组件
import {Radio} from 'antd'
复制代码
button.js及对应添加的样式以下所示
//[/src/pages/ui/button.js]
import React,{Component} from 'react';
import {Card,Button,Radio} from 'antd';
import './ui.less';
class Buttons extends Component{
constructor(props){
super(props);
this.state={
loading:true,
size:'default'
}
}
render(){
return(
<div>
<Card title="基础按钮" className="card-wrap">
{/*主按钮*/}
<Button type="primary">Imooc</Button>
<Button>Imooc</Button>
{/* 虚线按钮 */}
<Button type="dashed">Imooc</Button>
{/* 危险按钮 */}
<Button type="danger">Imooc</Button>
{/* 禁用按钮 */}
<Button disabled>Imooc</Button>
</Card>
<Card title="图形按钮" className="card-wrap">
{/*经过icon设定图标,shape设置形状*/}
<Button icon="plus">建立</Button>
<Button icon="edit">编辑</Button>
<Button icon="delete">删除</Button>
<Button icon="search" shape="circle"></Button>
<Button type="primary" icon="search">搜索</Button>
<Button type="primary" icon="download">下载</Button>
</Card>
<Card title="Loading按钮" className="card-wrap">
{/*经过loading属性为{true}表示加载中图标(此时按钮不能点)*/}
<Button type="primary" loading={this.state.loading}>肯定</Button>
<Button type="primary" shape="circle" loading={this.state.loading} ></Button>
<Button loading={this.state.loading} >点击加载</Button>
<Button shape="circle" loading={this.state.loading} ></Button>
<Button type="primary" onClick={this.handleCloseLoading}>关闭</Button>
</Card>
<Card title="按钮组">
<Button.Group>
<Button type="primary" icon="left">返回</Button>
<Button type="primary" icon="right">前进</Button>
</Button.Group>
</Card>
<Card title="按钮尺寸" className="card-wrap">
<Radio.Group value={this.state.size} onChange={this.handleChange}>
<Radio value="small">小</Radio>
<Radio value="default">中</Radio>
<Radio value="large">大</Radio>
</Radio.Group>
<Button type="primary" size={this.state.size}>Imooc</Button>
<Button size={this.state.size}>Imooc</Button>
<Button type="dashed" size={this.state.size}>Imooc</Button>
<Button type="danger" size={this.state.size}>imooc</Button>
</Card>
</div>
)
}
handleCloseLoading=()=>{
this.setState({
loading:false
});
}
handleChange=(e)=>{
this.setState({
size:e.target.value
});
}
}
export default Buttons;
//[/src/pages/ui/ui.less]
.card-wrap{
button{
margin-right: 10px;
}
}
复制代码
效果图
补充知识点
当Route页面内部信息超过当前页面大小时,会出现滚动条,左侧导航栏会跟着一块儿滚动,导航栏下方为空包
解决方案
当common.less中的main的定义添加overflow:auto,表示当渲染页面高度超过当前屏幕时,自动滚动
import {Modal} from 'antd';
复制代码
Model组件属性
title属性做为标题显示
visible属性参数为{true|false},为true则显示,为false则不显示
onCancel属性值为一个函数,执行当点击模态框的×或cancel选项时执行的方法
Model内部填写的内容将做为模板框的正文内容
知识点
组件的onClick值为this.handleName(即函数名)时,表示一开始就会自动调用,没法传参;当须要传参时,须要将onClick中的内容变为箭头函数,返回代参的调用方法从而实现点击时执行函数并传参调用方法
传递的参数若是想做为对象的键时,须要用[]进行包裹,若是没包裹直接放置键的位置则视为变量而报错
src/pages/ui/modal.js
import React from 'react'
import { Card, Button, Modal } from 'antd'
import './ui.less'
export default class Buttons extends React.Component {
state = {
showModal1: false,
showModal2: false,
showModal3: false,
showModal4: false
}
handleOpen = (type)=>{
this.setState({
[type]:true
})
}
handleConfirm = (type)=>{
Modal[type]({
title:'确认?',
content:'你肯定你学会了React了吗?',
onOk(){
console.log('Ok')
},
onCancel(){
console.log('Cancel')
}
})
}
render(){
return (
<div>
<Card title="基础模态框" className="card-wrap">
<Button type="primary" onClick={() =>this.handleOpen('showModal1')}>Open</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal2')}>自定义页脚</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal3')}>顶部20px弹框</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal4')}>水平垂直居中</Button>
</Card>
<Card title="信息确认框" className="card-wrap">
<Button type="primary" onClick={() => this.handleConfirm('confirm')}>Confirm</Button>
<Button type="primary" onClick={() => this.handleConfirm('info')}>Info</Button>
<Button type="primary" onClick={() => this.handleConfirm('success')}>Success</Button>
<Button type="primary" onClick={() => this.handleConfirm('warning')}>Warning</Button>
</Card>
<Modal
title="React"
visible={this.state.showModal1}
onCancel={()=>{
this.setState({
showModal1:false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
visible={this.state.showModal2}
okText="好的"
cancelText="算了"
onCancel={() => {
this.setState({
showModal2: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
style={{top:20}}
visible={this.state.showModal3}
onCancel={() => {
this.setState({
showModal3: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
wrapClassName="vertical-center-modal"
visible={this.state.showModal4}
onCancel={() => {
this.setState({
showModal4: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
</div>
);
}
}
复制代码
效果图
Model自定义页脚实现方式
Model组件的visible属性{true}或{false}实现是否显示
Model组件的okText属性设置OK选项的显示内容
Model组件的cancelText属性设置Cancel选项显示内容
Model顶部20px弹框实现方式
利用style属性值为{{top:20}}设定距顶部20px
Model水平居中实现方式
利用Model组件的wrapClassName设定样式名称
实例代码
import React from 'react'
import { Card, Button, Modal } from 'antd'
import './ui.less'
export default class Buttons extends React.Component {
state = {
showModal1: false,
showModal2: false,
showModal3: false,
showModal4: false
}
handleOpen = (type)=>{
this.setState({
[type]:true
})
}
handleConfirm = (type)=>{
Modal[type]({
title:'确认?',
content:'你肯定你学会了React了吗?',
onOk(){
console.log('Ok')
},
onCancel(){
console.log('Cancel')
}
})
}
render(){
return (
<div>
<Card title="基础模态框" className="card-wrap">
<Button type="primary" onClick={() =>this.handleOpen('showModal1')}>Open</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal2')}>自定义页脚</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal3')}>顶部20px弹框</Button>
<Button type="primary" onClick={() =>this.handleOpen('showModal4')}>水平垂直居中</Button>
</Card>
<Card title="信息确认框" className="card-wrap">
<Button type="primary" onClick={() => this.handleConfirm('confirm')}>Confirm</Button>
<Button type="primary" onClick={() => this.handleConfirm('info')}>Info</Button>
<Button type="primary" onClick={() => this.handleConfirm('success')}>Success</Button>
<Button type="primary" onClick={() => this.handleConfirm('warning')}>Warning</Button>
</Card>
<Modal
title="React"
visible={this.state.showModal1}
onCancel={()=>{
this.setState({
showModal1:false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
visible={this.state.showModal2}
okText="好的"
cancelText="算了"
onCancel={() => {
this.setState({
showModal2: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
style={{top:20}}
visible={this.state.showModal3}
onCancel={() => {
this.setState({
showModal3: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
<Modal
title="React"
wrapClassName="vertical-center-modal"
visible={this.state.showModal4}
onCancel={() => {
this.setState({
showModal4: false
})
}}
>
<p>欢迎学习慕课新推出的React高级课程</p>
</Modal>
</div>
);
}
}
复制代码
src/pages/form/login.js
import React from "react";
import { Card, Form, Input, Button, message, Icon, Checkbox } from "antd";
const FormItem = Form.Item;
class FormLogin extends React.Component{
handleSubmit = ()=>{
let userInfo = this.props.form.getFieldsValue();
this.props.form.validateFields((err,values)=>{
if(!err){
message.success(`${userInfo.userName} 恭喜你,您经过本次表单组件学习,当前密码为:${userInfo.userPwd}`)
}
})
}
render(){
const { getFieldDecorator } = this.props.form;
return (
<div>
<Card title="登陆行内表单">
<Form layout="inline">
<FormItem>
<Input placeholder="请输入用户名"/>
</FormItem>
<FormItem>
<Input placeholder="请输入密码" />
</FormItem>
<FormItem>
<Button type="primary">登陆</Button>
</FormItem>
</Form>
</Card>
<Card title="登陆水平表单" style={{marginTop:10}}>
<Form style={{width:300}}>
<FormItem>
{
getFieldDecorator('userName',{
initialValue:'',
rules:[
{
required:true,
message:'用户名不能为空'
},
{
min:5,max:10,
message:'长度不在范围内'
},
{
pattern:new RegExp('^\\w+$','g'),
message:'用户名必须为字母或者数字'
}
]
})(
<Input prefix={<Icon type="user"/>} placeholder="请输入用户名" />
)
}
</FormItem>
<FormItem>
{
getFieldDecorator('userPwd', {
initialValue: '',
rules: []
})(
<Input prefix={<Icon type="lock" />} type="password" placeholder="请输入密码" />
)
}
</FormItem>
<FormItem>
{
getFieldDecorator('remember', {
valuePropName:'checked',
initialValue: true
})(
<Checkbox>记住密码</Checkbox>
)
}
<a href="#" style={{float:'right'}}>忘记密码</a>
</FormItem>
<FormItem>
<Button type="primary" onClick={this.handleSubmit}>登陆</Button>
</FormItem>
</Form>
</Card>
</div>
);
}
}
export default Form.create()(FormLogin);
复制代码
若是不使用AntD封装好的方法来或许,常规的react 是先绑定onClick或onChange事件,经过事件里的方法获取target目标源,在获取目标源里的value值,而后把value值存起来,才能使用
AntD 经过getFieldDecorator 属性,来读取用户名及密码,直接进行使用,且使用以前必须经过Form.create()建立一个对象/表单,以后咱们才能使用getFieldDecorator
this.props.form.getFieldDecorator是AntD已经封装好的,固定语法,要记住
const { getFieldDecorator } = this.props.form;
复制代码
getFieldDecorator用法以下,其中在rules内定义规则
<FormItem>
{
getFieldDecorator('userName',{
initialValue:'',
rules:[
{
required:true,
message:'用户名不能为空'
},
{
min:5,max:10,
message:'长度不在范围内'
},
{
pattern:new RegExp('^\\w+$','g'),
message:'用户名必须为字母或数字',
}
]
})(
<Input prefix={<Icon type="user"/>} placeholder="请输入用户名"/>
)
}
</FormItem>
复制代码
必需要有下面这句话。经过Form.create()建立表单,将组件传递进去,这样才能识别 getFieldDecorator,不然会报错
//关键,必定要经过Form.create()建立一个对象/表单,将组件传递进去,这样才能识别 getFieldDecorator
export default Form.create()(FormLogin);
复制代码
handleSubmit = () =>{
//getFieldsValue是object对象,form的一个方法,用于获取表单下全部的值
let userInfo = this.props.form.getFieldsValue();
//validateFields方法是校验信息是否符合要求,校验下是一个循环,用()=>
this.props.form.validateFields((err,values)=>{
if(!err){
message.success(`${userInfo.userName}恭喜你,登录成功!当前密码为:${userInfo.userPwd}`)
}
})
}
复制代码
效果如图
必定要在getFieldDecorator里声明valuePropName:'checked',才能默认勾选Checkbox 的按钮
<FormItem>
{
getFieldDecorator('remember',{
valuePropName:'checked',
initialValue:true
})(
<Checkbox >记住密码</Checkbox>
)
}
<a href="#" style={{float:'right'}}>忘记密码</a>
</FormItem>
复制代码
使用prefix={}
<Input prefix={<Icon type="user"/>} placeholder="请输入用户名"/>
<Input prefix={<Icon type="lock"/>} placeholder="请输入密码"/>
复制代码
总体效果如图:
import React from 'react'
import {Card,Form,Button,Input,Checkbox,Radio,Select,Switch,DatePicker,TimePicker,Upload,Icon,message, InputNumber} from 'antd'
import moment from 'moment';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const Option = Select.Option;
const TextArea = Input.TextArea;
class FormRegister extends React.Component{
state={}
handleSubmit = ()=>{
let userInfo = this.props.form.getFieldsValue();
console.log(JSON.stringify(userInfo))
message.success(`${userInfo.userName} 恭喜你,您经过本次表单组件学习,当前密码为:${userInfo.userPwd}`)
}
getBase64 = (img, callback)=>{
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
this.getBase64(info.file.originFileObj, imageUrl => this.setState({
userImg:imageUrl,
loading: false,
}));
}
}
render(){
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol:{
xs:24,
sm:4
},
wrapperCol:{
xs:24,
sm:12
}
}
const offsetLayout = {
wrapperCol:{
xs:24,
sm:{
span:12,
offset:4
}
}
}
const rowObject = {
minRows: 4, maxRows: 6
}
return (
<div>
<Card title="注册表单">
<Form layout="horizontal">
<FormItem label="用户名" {...formItemLayout}>
{
getFieldDecorator('userName', {
initialValue: '',
rules: [
{
required: true,
message: '用户名不能为空'
}
]
})(
<Input placeholder="请输入用户名" />
)
}
</FormItem>
<FormItem label="密码" {...formItemLayout}>
{
getFieldDecorator('userPwd', {
initialValue: ''
})(
<Input type="password" placeholder="请输入密码" />
)
}
</FormItem>
<FormItem label="性别" {...formItemLayout}>
{
getFieldDecorator('sex', {
initialValue: '1'
})(
<RadioGroup>
<Radio value="1">男</Radio>
<Radio value="2">女</Radio>
</RadioGroup>
)
}
</FormItem>
<FormItem label="年龄" {...formItemLayout}>
{
getFieldDecorator('age', {
initialValue: 18
})(
<InputNumber />
)
}
</FormItem>
<FormItem label="当前状态" {...formItemLayout}>
{
getFieldDecorator('state', {
initialValue: '2'
})(
<Select>
<Option value="1">咸鱼一条</Option>
<Option value="2">风华浪子</Option>
<Option value="3">北大才子一枚</Option>
<Option value="4">百度FE</Option>
<Option value="5">创业者</Option>
</Select>
)
}
</FormItem>
<FormItem label="爱好" {...formItemLayout}>
{
getFieldDecorator('interest', {
initialValue: ['2','5']
})(
<Select mode="multiple">
<Option value="1">游泳</Option>
<Option value="2">打篮球</Option>
<Option value="3">踢足球</Option>
<Option value="4">跑步</Option>
<Option value="5">登山</Option>
<Option value="6">骑行</Option>
<Option value="7">桌球</Option>
<Option value="8">麦霸</Option>
</Select>
)
}
</FormItem>
<FormItem label="是否已婚" {...formItemLayout}>
{
getFieldDecorator('isMarried', {
valuePropName:'checked',
initialValue: true
})(
<Switch/>
)
}
</FormItem>
<FormItem label="生日" {...formItemLayout}>
{
getFieldDecorator('birthday',{
initialValue:moment('2018-08-08')
})(
<DatePicker
showTime
format="YYYY-MM-DD HH:mm:ss"
/>
)
}
</FormItem>
<FormItem label="联系地址" {...formItemLayout}>
{
getFieldDecorator('address',{
initialValue:'北京市海淀区奥林匹克公园'
})(
<TextArea
autosize={rowObject}
/>
)
}
</FormItem>
<FormItem label="早起时间" {...formItemLayout}>
{
getFieldDecorator('time')(
<TimePicker/>
)
}
</FormItem>
<FormItem label="头像" {...formItemLayout}>
{
getFieldDecorator('userImg')(
<Upload
listType="picture-card"
showUploadList={false}
action="//jsonplaceholder.typicode.com/posts/"
onChange={this.handleChange}
>
{this.state.userImg?<img src={this.state.userImg}/>:<Icon type="plus"/>}
</Upload>
)
}
</FormItem>
<FormItem {...offsetLayout}>
{
getFieldDecorator('userImg')(
<Checkbox>我已阅读过<a href="#">慕课协议</a></Checkbox>
)
}
</FormItem>
<FormItem {...offsetLayout}>
<Button type="primary" onClick={this.handleSubmit}>注册</Button>
</FormItem>
</Form>
</Card>
</div>
);
}
}
export default Form.create()(FormRegister);
复制代码
<FormItem label="联系地址" {...formItemLayout}>
{
getFieldDecorator('address',{
initialValue:'北京市海淀区奥林匹克公园'
})(
<TextArea
autosize={rowObject}
/>
)
}
</FormItem>
复制代码
效果如图:
同时显示日期和时间 要showTime 和 format=‘YYYY–MM–DD HH:mm:ss’同时使用
<FormItem label="生日" {...formItemLayout}>
{
getFieldDecorator('birthday',{
initialValue:moment('2018-08-08')
})(
<DatePicker
showTime
format="YYYY-MM-DD HH:mm:ss"
/>
)
}
</FormItem>
复制代码
如图
实际作项目中,与如今有所不一样
要用本身的接口替换 action="//jsonplaceholder.typicode.com/posts/"里的内容
不须要 getBase64 方法,直从接口返回上传成功的URL地址,服务端把图片存到服务器里,返回前段一个服务器的图片地址,咱们把图片地址放在img的src展现
antD上传头像使用.jpg的图片格式
//获取文件格式的字符串
getBase64 = (img, callback)=>{
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
this.getBase64(info.file.originFileObj, imageUrl => this.setState({
userImg:imageUrl,
loading: false,
}));
}
}
<FormItem label="头像" {...formItemLayout}>
{
getFieldDecorator('userImg',{
})(
<Upload
listType="picture-card"
showUploadList={false}
action="//jsonplaceholder.typicode.com/posts/"
onChange={this.handleChange}
>
{this.state.userImg?<img src={this.state.userImg} />:<Icon type="plus"/>}
</Upload>
)
}
</FormItem>
复制代码
src/pages/table/basicTable.js
import React from 'react';
import { Card, Table, Modal, Button, message} from 'antd';
import axios from './../../axios/index'
import Utils from './../../utils/utils';
export default class BasicTable extends React.Component{
state={
dataSource2:[]
}
params = {
page:1
}
componentDidMount(){
const data = [
{
id:'0',
userName:'Jack',
sex:'1',
state:'1',
interest:'1',
birthday:'2000-01-01',
address:'北京市海淀区奥林匹克公园',
time:'09:00'
},
{
id: '1',
userName: 'Tom',
sex: '1',
state: '1',
interest: '1',
birthday: '2000-01-01',
address: '北京市海淀区奥林匹克公园',
time: '09:00'
},
{
id: '2',
userName: 'Lily',
sex: '1',
state: '1',
interest: '1',
birthday: '2000-01-01',
address: '北京市海淀区奥林匹克公园',
time: '09:00'
},
]
data.map((item,index)=>{
item.key = index;
})
this.setState({
dataSource: data
})
this.request();
}
// 动态获取mock数据
request = ()=>{
let _this = this;
axios.ajax({
url:'/table/list',
data:{
params:{
page:this.params.page
}
}
}).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;
this.request();
})
})
}
})
}
onRowClick = (record,index)=>{
let selectKey = [index];
Modal.info({
title:'信息',
content:`用户名:${record.userName},用户爱好:${record.interest}`
})
this.setState({
selectedRowKeys:selectKey,
selectedItem: record
})
}
// 多选执行删除动做
handleDelete = (()=>{
let rows = this.state.selectedRows;
let ids = [];
rows.map((item)=>{
ids.push(item.id)
})
Modal.confirm({
title:'删除提示',
content: `您肯定要删除这些数据吗?${ids.join(',')}`,
onOk:()=>{
message.success('删除成功');
this.request();
}
})
})
render(){
const columns = [
{
title:'id',
key:'id',
dataIndex:'id'
},
{
title: '用户名',
key: 'userName',
dataIndex: 'userName'
},
{
title: '性别',
key: 'sex',
dataIndex: 'sex',
render(sex){
return sex ==1 ?'男':'女'
}
},
{
title: '状态',
key: 'state',
dataIndex: 'state',
render(state){
let config = {
'1':'咸鱼一条',
'2':'风华浪子',
'3':'北大才子',
'4':'百度FE',
'5':'创业者'
}
return config[state];
}
},
{
title: '爱好',
key: 'interest',
dataIndex: 'interest',
render(abc) {
let config = {
'1': '游泳',
'2': '打篮球',
'3': '踢足球',
'4': '跑步',
'5': '登山',
'6': '骑行',
'7': '桌球',
'8': '麦霸'
}
return config[abc];
}
},
{
title: '生日',
key: 'birthday',
dataIndex: 'birthday'
},
{
title: '地址',
key: 'address',
dataIndex: 'address'
},
{
title: '早起时间',
key: 'time',
dataIndex: 'time'
}
]
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
type:'radio',
selectedRowKeys
}
const rowCheckSelection = {
type: 'checkbox',
selectedRowKeys,
onChange:(selectedRowKeys,selectedRows)=>{
this.setState({
selectedRowKeys,
selectedRows
})
}
}
return (
<div>
<Card title="基础表格">
<Table
bordered
columns={columns}
dataSource={this.state.dataSource}
pagination={false}
/>
</Card>
<Card title="动态数据渲染表格-Mock" style={{margin:'10px 0'}}>
<Table
bordered
columns={columns}
dataSource={this.state.dataSource2}
pagination={false}
/>
</Card>
<Card title="Mock-单选" style={{ margin: '10px 0' }}>
<Table
bordered
rowSelection={rowSelection}
onRow={(record,index) => {
return {
onClick:()=>{
this.onRowClick(record,index);
}
};
}}
columns={columns}
dataSource={this.state.dataSource2}
pagination={false}
/>
</Card>
<Card title="Mock-单选" style={{ margin: '10px 0' }}>
<div style={{marginBottom:10}}>
<Button onClick={this.handleDelete}>删除</Button>
</div>
<Table
bordered
rowSelection={rowCheckSelection}
columns={columns}
dataSource={this.state.dataSource2}
pagination={false}
/>
</Card>
<Card title="Mock-表格分页" style={{ margin: '10px 0' }}>
<Table
bordered
columns={columns}
dataSource={this.state.dataSource2}
pagination={this.state.pagination}
/>
</Card>
</div>
);
}
}
复制代码
点击行内任意一处,均有提示
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);
}
})
});
}
}
复制代码
render函数return内容:
<Card title="Mock-表格分页" style={{margin:'10px 0'}}>
<Table
bordered
columns = {columns }
dataSource={this.state.dataSource2}
pagination={this.state.pagination}
/>
</Card>
复制代码
逻辑代码
state = {
dataSource2:[]
}
params = {
page:1
}
componentDidMount(){
this.request();
}
//动态获取mock数据
request = () =>{
// console.log(111)
let _this = this;
axios.ajax({
url:'/table/list',
data:{
params:{
page:this.params.page
},
}
}).then((res)=>{
if (res.code == 0){
res.result.list.map((item,index)=>{
item.key = index;
})
this.setState({
dataSource2:res.result.list,
selectedRowKeys:[],
// selectedIds:ids
selectedRows:null,
//分页
pagination:Utils.pagination(res,(current)=>{
//to-do
_this.params.page = current;
console.log(current)
this.request();
// console.log('333')
})
})
}
})
}
复制代码
Loading:加载数据成功前loading功能
static ajax(options){
let loading ;
//发送请求前加载
if(options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
//block显示loading
loading.style.display = 'block';
}
let baseApi = 'https://www.easy-mock.com/mock/5c84ccabcfb6692c295163b4/BikeSharingAPI'
return new Promise((resolve,reject)=>{
axios({
url:options.url,
method:'get',
baseURL:baseApi,
timeout:10000,
params:(options.data && options.data.params) || "",
}).then((response)=>{
//请求成功后关闭loading
if(options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
//none 关闭loading
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)
}
})
});
}
复制代码
发送ajax时isShowLoading:true,也能够不写
axios.ajax({
url:'/table/list',
data:{
params:{
page:this.params.page
},
//不想有loading时。默认为true
// isShowLoading :false,
}
}).then((res)=>{
})
}
复制代码
发送请求前加载
//发送请求前加载
if(options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
//block显示loading
loading.style.display = 'block';
}
复制代码
请求成功后关闭loading
//请求成功后关闭loading
if(options.data && options.data.isShowLoading !== false){
loading = document.getElementById('ajaxLoading');
//none 关闭loading
loading.style.display = 'none';
}
复制代码
注意:不想有loading时,要设置ShowLoading:false,默认为true
axios.ajax({
url:'/table/list',
data:{
params:{
page:this.params.page
},
//不想有loading时。默认为true
// isShowLoading :false,
}
}).then((res)=>{
})
}
复制代码
render (){
const {selectedRowKeys} = this.state
const rowSelection = {
type:'radio',
//必写。告诉table组件,选中了那个/那些行
selectedRowKeys
}
<Card title="Mock-单选" style={{margin:'10px 0'}}>
<Table
bordered
rowSelection={rowSelection}
onRow={(record,index) => {
return {
onClick: () => {
this.onRowClick(record,index)
}, // 点击行
};
}}
columns = {columns }
dataSource={this.state.dataSource2}
pagination={false}
/>
</Card>
}
复制代码
render上的逻辑代码
//获取每一行里的 item值
onRowClick = (record,index) =>{
//多选时用数组
let selectKey = [index ];
Modal.info({
title:'信息',
content:`用户名:${record.userName},用户爱好:${record.interest}`
});
this.setState({
selectedRowKeys:selectKey,
//获取item值 可对单个行进行新增删除操做。
selectedRowItem:record
})
}
复制代码
//多选删除事件
handleDelete = () =>{
let rows = this.state.selectedRows;
let ids = [];
rows.map((item,index)=>{
ids.push(item.id)
})
Modal.confirm({
title:'提示',
content:`您肯定要删除这些数据吗?${ids.join(',')}`,
onOk:()=>{
message.success('删除成功');
this.request();
}
})
}
render (){
const {selectedRowKeys} = this.state
const rowCheckSelection = {
type:'checkbox',
selectedRowKeys,
onChange:(selectedRowKeys,selectedRows) =>{
// let ids = [];
// selectedRowKeys.map((item,index)=>{
// item.key = index;
// ids.push(item.id)
// })
this.setState({
//必写。告诉table组件,选中了那个/那些行
selectedRowKeys,
// selectedIds:ids
selectedRows
})
}
}
return(
<div>
<Card title="Mock-多选" style={{margin:'10px 0'}}>
<div style={{marginBottom:10}}>
<Button onClick={this.handleDelete}>删除</Button>
</div>
<Table
bordered
rowSelection={rowCheckSelection}
columns = {columns }
dataSource={this.state.dataSource2}
pagination={false}
/>
</Card>
</div>
)
}
复制代码
import React from 'react';
import { Card, Table, Modal, Button, message, Badge } from 'antd';
import axios from './../../axios/index'
import Utils from './../../utils/utils';
export default class BasicTable extends React.Component {
state = {
}
params = {
page:1
}
componentDidMount(){
this.request();
}
// 动态获取mock数据
request = () => {
let _this = this;
axios.ajax({
url: '/table/high/list',
data: {
params: {
page: this.params.page
}
}
}).then((res) => {
if (res.code == 0) {
res.result.list.map((item, index) => {
item.key = index;
})
this.setState({
dataSource: res.result.list
})
}
})
}
handleChange = (pagination, filters, sorter)=>{
console.log("::" + 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,
dataIndex: 'id'
},
{
title: '用户名',
key: 'userName',
width: 80,
dataIndex: 'userName'
},
{
title: '性别',
key: 'sex',
width: 80,
dataIndex: 'sex',
render(sex) {
return sex == 1 ? '男' : '女'
}
},
{
title: '状态',
key: 'state',
width: 80,
dataIndex: 'state',
render(state) {
let config = {
'1': '咸鱼一条',
'2': '风华浪子',
'3': '北大才子',
'4': '百度FE',
'5': '创业者'
}
return config[state];
}
},
{
title: '爱好',
key: 'interest',
width: 80,
dataIndex: 'interest',
render(abc) {
let config = {
'1': '游泳',
'2': '打篮球',
'3': '踢足球',
'4': '跑步',
'5': '登山',
'6': '骑行',
'7': '桌球',
'8': '麦霸'
}
return config[abc];
}
},
{
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
},
{
title: '地址',
key: 'address',
width: 120,
dataIndex: 'address'
},
{
title: '早起时间',
key: 'time',
width: 80,
dataIndex: 'time'
}
]
const columns2 = [
{
title: 'id',
key: 'id',
width: 80,
fixed:'left',
dataIndex: 'id'
},
{
title: '用户名',
key: 'userName',
width: 80,
fixed: 'left',
dataIndex: 'userName'
},
{
title: '性别',
key: 'sex',
width: 80,
dataIndex: 'sex',
render(sex) {
return sex == 1 ? '男' : '女'
}
},
{
title: '状态',
key: 'state',
width: 80,
dataIndex: 'state',
render(state) {
let config = {
'1': '咸鱼一条',
'2': '风华浪子',
'3': '北大才子',
'4': '百度FE',
'5': '创业者'
}
return config[state];
}
},
{
title: '爱好',
key: 'interest',
width: 80,
dataIndex: 'interest',
render(abc) {
let config = {
'1': '游泳',
'2': '打篮球',
'3': '踢足球',
'4': '跑步',
'5': '登山',
'6': '骑行',
'7': '桌球',
'8': '麦霸'
}
return config[abc];
}
},
{
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
},
{
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
}, {
title: '生日',
key: 'birthday',
width: 120,
dataIndex: 'birthday'
},
{
title: '地址',
key: 'address',
width: 120,
fixed: 'right',
dataIndex: 'address'
},
{
title: '早起时间',
key: 'time',
width: 80,
fixed: 'right',
dataIndex: 'time'
}
]
const columns3 = [
{
title: 'id',
key: 'id',
dataIndex: 'id'
},
{
title: '用户名',
key: 'userName',
dataIndex: 'userName'
},
{
title: '性别',
key: 'sex',
dataIndex: 'sex',
render(sex) {
return sex == 1 ? '男' : '女'
}
},
{
title: '年龄',
key: 'age',
dataIndex: 'age',
sorter:(a,b)=>{
return a.age - b.age;
},
sortOrder:this.state.sortOrder
},
{
title: '状态',
key: 'state',
dataIndex: 'state',
render(state) {
let config = {
'1': '咸鱼一条',
'2': '风华浪子',
'3': '北大才子',
'4': '百度FE',
'5': '创业者'
}
return config[state];
}
},
{
title: '爱好',
key: 'interest',
dataIndex: 'interest',
render(abc) {
let config = {
'1': '游泳',
'2': '打篮球',
'3': '踢足球',
'4': '跑步',
'5': '登山',
'6': '骑行',
'7': '桌球',
'8': '麦霸'
}
return config[abc];
}
},
{
title: '生日',
key: 'birthday',
dataIndex: 'birthday'
},
{
title: '地址',
key: 'address',
dataIndex: 'address'
},
{
title: '早起时间',
key: 'time',
dataIndex: 'time'
}
]
const columns4 = [
{
title: 'id',
dataIndex: 'id'
},
{
title: '用户名',
dataIndex: 'userName'
},
{
title: '性别',
dataIndex: 'sex',
render(sex) {
return sex == 1 ? '男' : '女'
}
},
{
title: '年龄',
dataIndex: 'age'
},
{
title: '状态',
dataIndex: 'state',
render(state) {
let config = {
'1': '咸鱼一条',
'2': '风华浪子',
'3': '北大才子',
'4': '百度FE',
'5': '创业者'
}
return config[state];
}
},
{
title: '爱好',
dataIndex: 'interest',
render(abc) {
let config = {
'1': <Badge status="success" text="成功"/>,
'2': <Badge status="error" text="报错" />,
'3': <Badge status="default" text="正常" />,
'4': <Badge status="processing" text="进行中" />,
'5': <Badge status="warning" text="警告" />
}
return config[abc];
}
},
{
title: '生日',
dataIndex: 'birthday'
},
{
title: '地址',
dataIndex: 'address'
},
{
title: '操做',
render:(text,item)=>{
return <Button size="small" onClick={(item) => { this.handleDelete(item) }}>删除</Button>
}
}
]
return (
<div>
<Card title="头部固定">
<Table
bordered
columns={columns}
dataSource={this.state.dataSource}
pagination={false}
scroll={{y:240}}
/>
</Card>
<Card title="左侧固定" style={{ margin: '10px 0' }}>
<Table
bordered
columns={columns2}
dataSource={this.state.dataSource}
pagination={false}
scroll={{ x: 2650 }}
/>
</Card>
<Card title="表格排序" style={{ margin: '10px 0' }}>
<Table
bordered
columns={columns3}
dataSource={this.state.dataSource}
pagination={false}
onChange={this.handleChange}
/>
</Card>
<Card title="操做按钮" style={{ margin: '10px 0' }}>
<Table
bordered
columns={columns4}
dataSource={this.state.dataSource}
pagination={false}
/>
</Card>
</div>
)
}
}
复制代码
y:y轴、240:y轴长度
<Card title="头部固定">
<Table
bordered
columns={columns }
dataSource={this.state.dataSource}
pagination={false}
scroll={{y:240}}
/>
</Card>
复制代码
table标签里设置scroll={{scroll={{x:1050}}
x:x轴、1050:x轴总宽度,width之和
<Card title="左侧固定" style={{margin: '10px 0'}}>
<Table
bordered
columns={columns2 }
dataSource={this.state.dataSource}
pagination={false}
scroll={{x:1050}}
/>
</Card>
复制代码
在标题栏下设置fixed:'left';fixed:'right'
const columns2 = [
{
title: 'id',
dataIndex: 'id',
width:80,
fixed:'left'
},
{
title: '用户名',
dataIndex: 'userName',
fixed:'left',
width:80,
},
{
title: '性别',
dataIndex: 'sex',
render(sex){
return sex == 1 ? '男' : '女'
},
width:80,
},
{
title: '生日',
width:120,
dataIndex: 'birthday'
},
{
title: '早起时间',
dataIndex: 'time',
width:80,
},
{
title: '地址',
width:120,
fixed:'right',
dataIndex: 'address',
},
]
复制代码
若中间有缝隙,可增长标题种类
render() {
const columns3 = [
{
title: 'id',
dataIndex: 'id',
width:80,
fixed:'left'
},
{
title: '用户名',
dataIndex: 'userName',
fixed:'left',
width:80,
},
{
title: '性别',
dataIndex: 'sex',
render(sex){
return sex == 1 ? '男' : '女'
},
},
{
title: '年龄',
dataIndex: 'age',
width:80,
//升序 降序 功能实现 加两个参数
sorter:(a,b)=>{
return a.age-b.age;
},
sortOrder:this.state.sortOrder
},
{
title: '状态',
dataIndex: 'state',
width:80,
render(state){
let config = {
'1': '咸鱼一条',
'2': '天才',
'3': '最强大脑',
'4': '码奴',
'5': '旅行家',
}
return config[state]
}
},
{
title: '爱好',
width:80,
dataIndex: 'interest',
render(ABC){
let config = {
'1': '游泳',
'2': '打球',
'3': '慢跑',
'4': '听歌',
'5': '看书',
'6': '看电影',
'7': '旅行',
'8': '学习',
}
return config[ABC]
}
},
{
title: '生日',
dataIndex: 'birthday'
},
{
title: '地址',
width:120,
dataIndex: 'address',
},
{
title: '早起时间',
dataIndex: 'time',
},
{
title: '生日',
width:120,
dataIndex: 'birthday'
},
{
title: '早起时间',
dataIndex: 'time',
width:80,
},
{
title: '地址',
width:120,
fixed:'right',
dataIndex: 'address',
},
]
return (
<div>
<Card title="年龄排序" style={{margin: '10px 0'}}>
<Table
bordered
columns={columns3 }
dataSource={this.state.dataSource}
pagination={false}
scroll={{x:1100}}
onChange={this.handleChange}
/>
</Card>
</div>
)
}
复制代码
升序/降序功能的实现,加两个参数sorter和sortOrder
{
title: '年龄',
dataIndex: 'age',
width:80,
//升序 降序 功能实现 加两个参数
sorter:(a,b)=>{
return a.age-b.age;
},
sortOrder:this.state.sortOrder
},
复制代码
逻辑代码
handleChange = (pagination,filers,sorter)=> {
console.log(sorter)
this.setState({
sortOrder: sorter.order
})
}
复制代码
render() {
const columns4 = [
{
title: 'id',
dataIndex: 'id',
fixed:'left'
},
{
title: '用户名',
dataIndex: 'userName',
},
{
title: '性别',
render(sex){
return sex == 1 ? '男' : '女'
},
},
{
title: '年龄',
dataIndex: 'age',
},
{
title: '状态',
dataIndex: 'state',
render(state){
let config = {
'1': '咸鱼一条',
'2': '天才',
'3': '最强大脑',
'4': '码奴',
'5': '旅行家',
}
return config[state]
}
},
{
title: '爱好',
dataIndex: 'interest',
render(ABC){
let config = {
'1': <Badge status="success" text="成功"/>,
'2': <Badge status="error" text="报错"/>,
'3': <Badge status="default" text="正常"/>,
'4': <Badge status="processing" text="进行中"/>,
'5': <Badge status="warning" text="警告"/>,
'6': <Badge status="default" text="正常"/>,
'7': <Badge status="processing" text="进行中"/>,
'8': <Badge status="warning" text="警告"/>,
}
return config[ABC]
}
},
{
title: '生日',
dataIndex: 'birthday'
},
{
title: '地址',
dataIndex: 'address',
},
{
title: '早起时间',
dataIndex: 'time',
},
{
title: '操做',
dataIndex: 'time',
//注意使用箭头函数 不然会找不到下面的this
render:(text,item)=>{
return <Button size='small'onClick={(item)=>{this.handleDelete(item)}}>删除</Button>
}
},
]
return (
<div>
<Card title="操做按钮" style={{margin: '10px 0'}}>
<Table
bordered
columns={columns4}
dataSource={this.state.dataSource}
pagination={false}
onChange={this.handleDelete}
/>
</Card>
</div>
)
}
复制代码
逻辑代码
//删除操做
handleDelete = (item) =>{
let id = item.id;
Modal.confirm({
title:'确认',
content:'您确认要删除磁条数据吗?',
onOK:()=>{
message.success('删除成功');
this.request();
}
})
}
复制代码
Badge样式:
{
title: '爱好',
dataIndex: 'interest',
render(ABC){
let config = {
'1': <Badge status="success" text="成功"/>,
'2': <Badge status="error" text="报错"/>,
'3': <Badge status="default" text="正常"/>,
'4': <Badge status="processing" text="进行中"/>,
'5': <Badge status="warning" text="警告"/>,
'6': <Badge status="default" text="正常"/>,
'7': <Badge status="processing" text="进行中"/>,
'8': <Badge status="warning" text="警告"/>,
}
return config[ABC]
}
},
复制代码
富文本编辑器Rich Text Editor,简称RTE,是一种可内嵌于浏览器,所见即所得的文本编辑器
react-draft-wysiwyg:文本编辑器插件
draftjs-to-html:文本转换为html的插件
yarn add react-draft-wysiwyg draftjs-to-html --save
复制代码
pages-rich-index.js:对应路由/admin/rich
import React from 'react'
import {Button,Card,Modal} from 'antd'
import {Editor} from 'react-draft-wysiwyg'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import draftjs from 'draftjs-to-html'
export default class RichText extends React.Component{
state = {
showRichText:false,
editorContent: '',
editorState: '',
};
handleClearContent = ()=>{ //清空文本
this.setState({
editorState:''
})
}
handleGetText = ()=>{ //获取文本内容
this.setState({
showRichText:true
})
}
onEditorChange = (editorContent) => { //编辑器的状态
this.setState({
editorContent,
});
};
onEditorStateChange = (editorState) => { //编辑器内容状态
this.setState({
editorState
});
};
render(){
const { editorContent, editorState } = this.state;
return (
<div>
<Card style={{marginTop:10}}>
<Button type="primary" onClick={this.handleClearContent}>清空内容</Button>
<Button type="primary" onClick={this.handleGetText}>获取HTML文本</Button>
</Card>
<Card title="富文本编辑器">
<Editor
editorState={editorState}
onContentStateChange={this.onEditorChange}
onEditorStateChange={this.onEditorStateChange}
/>
</Card>
<Modal
title="富文本"
visible={this.state.showRichText}
onCancel={()=>{
this.setState({
showRichText:false
})
}}
footer={null}
>
{draftjs(this.state.editorContent)}
</Modal>
</div>
);
}
}
复制代码
{
"code": 0,
"msg": "",
"result": {
"item_list|10": [{
"id|+1": 1,
"name": "@city",
"mode|1-2": 1,
"op_mode|1-2": 1,
"franchisee_id": 77,
"franchisee_name": "松果自营",
"city_admins|1-2": [{
"user_name": "@cname",
"user_id|+1": 10001
}],
"open_time": "@datetime",
"sys_user_name": "@cname",
"update_time": 1546580667000
}]
},
page: 1,
page_size: 10,
total: 20
}
复制代码
class FilterForm extends React.Component{
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>
<Option value="2">天津市</Option>
<Option value="3">深圳市</Option>
</Select>
)
}
</FormItem>
<FormItem label="用车模式">
{
getFieldDecorator('mode')(
<Select
style={{ width: 120 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">指定停车点模式</Option>
<Option value="2">禁停区模式</Option>
</Select>
)
}
</FormItem>
<FormItem label="营运模式">
{
getFieldDecorator('op_mode')(
<Select
style={{ width: 80 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">自营</Option>
<Option value="2">加盟</Option>
</Select>
)
}
</FormItem>
<FormItem label="加盟商受权状态">
{
getFieldDecorator('auth_status')(
<Select
style={{ width: 100 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">已受权</Option>
<Option value="2">未受权</Option>
</Select>
)
}
</FormItem>
<FormItem>
<Button type="primary" style={{margin:'0 20px'}}>查询</Button>
<Button>重置</Button>
</FormItem>
</Form>
);
}
}
FilterForm = Form.create({})(FilterForm);
复制代码
调用this.requestList(),默认请求接口数据
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; // 注:这里要返回item的值
});
this.setState({
list:list,
pagination:Utils.pagination(res,(current)=>{
_this.params.page = current;
_this.requestList();
})
})
})
}
render(){
...
}
复制代码
[开通城市]按钮:监听 onClick 事件,调用this.handleOpenCity()显示弹框
state = {
list:[],
isShowOpenCity:false // 默认不可见
}
// 开通城市
handleOpenCity = ()=>{
this.setState({
isShowOpenCity:true
})
}
复制代码
Modal部分默认隐藏,触发事件进行显示
onOk:点击肯定回调,参数为关闭函数
onCancel:取消回调,参数为关闭
<Modal
title="开通城市"
visible={this.state.isShowOpenCity}
onCancel={()=>{
this.setState({
isShowOpenCity:false
})
}}
onOk={this.handleSubmit}
>
<OpenCityForm wrappedComponentRef={(inst)=>{this.cityForm = inst;}}/>
</Modal>
复制代码
拿到表单中的信息对象inst,经过this.cityForm存到state中
通过Form.create之和若是要拿到ref,可使用rc-form提供的wrappedComponentRef,详细内容能够查看这里
class CustomizedForm extends React.Component {...}
// use wrappedComponentRef
const EnhancedForm = Form.create()(CustomizedFrom);
<EnhancedFrom wrappedComponentRef={(form)=>this.form=from}/>
this.form // => The instance of CustomizedForm
复制代码
城市开通提交
// 城市开通提交
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();
}
})
}
复制代码
弹框子组件二:开通城市表单
class OpenCityForm extends React.Component {
render() {
const formItemLayout = {
labelCol: {
span: 5
},
wrapperCol: {
span: 19
}
};
const {getFieldDecorator} = this.props.form;
return (
<Form layout="horizontal">
<FormItem label="选择城市" {...formItemLayout}>
{
getFieldDecorator('city_id', {
initialValue: '1'
})(
<Select style={{width: 100}}>
<Option value="">所有</Option>
<Option value="1">北京市</Option>
<Option value="2">天津市</Option>
</Select>
)
}
</FormItem>
<FormItem label="营运模式" {...formItemLayout}>
{
getFieldDecorator('op_mode', {
initialValue: '1'
})(
<Select style={{width: 100}}>
<Option value="1">自营</Option>
<Option value="2">加盟</Option>
</Select>
)
}
</FormItem>
<FormItem label="用车模式" {...formItemLayout}>
{
getFieldDecorator('use_mode', {
initialValue: '1'
})(
<Select style={{width: 100}}>
<Option value="1">指定停车点</Option>
<Option value="2">禁停区</Option>
</Select>
)
}
</FormItem>
</Form>
);
}
}
OpenCityForm = Form.create({})(OpenCityForm);
复制代码
Easy Mock城市管理的数据接口:
{
"code": 0,
"list": "开通成功"
}
复制代码
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 (
<div>
<Card>
<FilterForm />
</Card>
<Card style={{marginTop:10}}>
<Button type="primary" onClick={this.handleOpenCity}>开通城市</Button>
</Card>
<div className="content-wrap">
<Table
bordered
columns={columns}
dataSource={this.state.list}
pagination={this.state.pagination}
/>
</div>
<Modal
title="开通城市"
visible={this.state.isShowOpenCity}
onCancel={()=>{
this.setState({
isShowOpenCity:false
})
}}
onOk={this.handleSubmit}
>
<OpenCityForm wrappedComponentRef={(inst)=>{this.cityForm = inst;}}/>
</Modal>
</div>
);
}
}
class FilterForm extends React.Component{
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>
<Option value="2">天津市</Option>
<Option value="3">深圳市</Option>
</Select>
)
}
</FormItem>
<FormItem label="用车模式">
{
getFieldDecorator('mode')(
<Select
style={{ width: 120 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">指定停车点模式</Option>
<Option value="2">禁停区模式</Option>
</Select>
)
}
</FormItem>
<FormItem label="营运模式">
{
getFieldDecorator('op_mode')(
<Select
style={{ width: 80 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">自营</Option>
<Option value="2">加盟</Option>
</Select>
)
}
</FormItem>
<FormItem label="加盟商受权状态">
{
getFieldDecorator('auth_status')(
<Select
style={{ width: 100 }}
placeholder="所有"
>
<Option value="">所有</Option>
<Option value="1">已受权</Option>
<Option value="2">未受权</Option>
</Select>
)
}
</FormItem>
<FormItem>
<Button type="primary" style={{margin:'0 20px'}}>查询</Button>
<Button>重置</Button>
</FormItem>
</Form>
);
}
}
FilterForm = Form.create({})(FilterForm);
class OpenCityForm extends React.Component{
render(){
const formItemLayout = {
labelCol:{
span:5
},
wrapperCol:{
span:19
}
}
const { getFieldDecorator } =this.props.form;
return (
<Form layout="horizontal">
<FormItem label="选择城市" {...formItemLayout}>
{
getFieldDecorator('city_id',{
initialValue:'1'
})(
<Select style={{ width: 100 }}>
<Option value="">所有</Option>
<Option value="1">北京市</Option>
<Option value="2">天津市</Option>
</Select>
)
}
</FormItem>
<FormItem label="营运模式" {...formItemLayout}>
{
getFieldDecorator('op_mode', {
initialValue: '1'
})(
<Select style={{ width: 100 }}>
<Option value="1">自营</Option>
<Option value="2">加盟</Option>
</Select>
)
}
</FormItem>
<FormItem label="用车模式" {...formItemLayout}>
{
getFieldDecorator('use_mode', {
initialValue: '1'
})(
<Select style={{ width: 100 }}>
<Option value="1">指定停车点</Option>
<Option value="2">禁停区</Option>
</Select>
)
}
</FormItem>
</Form>
);
}
}
OpenCityForm = Form.create({})(OpenCityForm);
复制代码