本文基於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',
},
]
複製代碼
若中間有縫隙,可增長標題種類
若中間有過分太窄,修改scroll={{x:1050}},修改width之和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);
複製代碼