本文基於React共享單車後臺管理系統的開發進行學習記錄,內含React生態系統的總體介紹及相關內容整理,方便本身查閱,也但願對小夥伴有所幫助css
Facebook開源的一個JavaScript庫html
React結合生態構成的一個MV* 框架前端
Vue生態:node
Vue+Vue-Router+Vuex+Axios+Babel+Webpack
React生態:react
React+React-Router-Redux+Axios+Babel+Webpack
getDefaultPropswebpack
經過這個方法,來初始化一個Props屬性,Props就來自於父組件ios
getInitialStategit
初始化state,能夠直接在constructor中定義this.statees6
componentwillMountgithub
組件加載時只調用,之後組件更新不調用,整個生命週期只調用一次,此時能夠修改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 //安裝依賴包
源碼地址:https://github.com/shifengmin...
安裝腳手架
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」屬性或方法
<font face="楷體">例子使用:
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> ) } }
<font face="楷體">在public文件夾下添加assets文件夾,放置logo-ant.svg圖片
<font face="楷體">注意:public文件是build文件是build以後存儲內容的文件,一般內部放置靜態資源,public下的文件內容在訪問時全是經過根目錄直接訪問文件地址
<font face="楷體">編寫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;
<font face="楷體">在config下編寫manuConfig.js返回導航欄內容title和key
<font face="楷體">效果以下:
<font face="楷體">利用jsonp能夠解決跨域問題(所謂跨域就是跨域名,跨端口,跨協議)
web頁面上調用js文件時則不受跨域影響(且凡有src屬性的標籤都具備跨域能力,如<script> <img> <iframe>等)
<font face="楷體" color=#CD2626>Promise最大好處
<font face="楷體">將回調函數的異步調用變成鏈式調用。函數返回的promise對象參數接受一個函數,參數1爲成功resolve回調函數,參數2是失敗的reject回調函數,函數體中返回對應函數調用。調用該返回Promise對象的函數的then方法參數爲resolve函數傳入的屬性值爲參數的函數,在方法體內寫入回調成功返回的內容
src/components/Header/index.js
<font face="楷體">利用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版本已不須要路由配置,一切皆組件
<font face="楷體">提供了一些router的核心api,包括Router,Route,Switch等
<font face="楷體">提供了BrowserRouter,HashRouter,Route,Link,NavLink
<font face="楷體">安裝:npm install react-router-dom --save 或 yarn add react-router-dom
<font face="楷體">HashRouter和BrowserRouter
<font face="楷體">Route:path、exact、component、render
<font face="楷體">注:exact屬性表明精準匹配,必須path徹底匹配才能夠加載對應組件
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">
<font face="楷體">HashRouter將Link和Router進行包裹,其內部必須只能有一個子節點
<font face="楷體">經過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;
配置化實現路由功能
<font face = "楷體">建立Home.js內部寫上ul->li->Link導航組件,並在想要顯示對應路由內容的區域寫上{this.props.children}即會加載調用Home.js組建時內部傳遞的信息
<font face = "楷體">建立router.js,最外層用HashRouter包裹,第二級用Home組件包裹,內部寫對應路由Route(path路由路徑匹配、及component屬性渲染的組件)
<font face = "楷體">執行流程,index.js渲染Router組件時,因爲最外層是HashRouter,內部是Home組件故加載Home組件內容,並經過{this.props.children}獲得在調用Home組件時內部的信息
嵌套路由
<font face = "楷體">如想實如今Main組件中的嵌套路由,須要在Main組件中添加{this.props.children}從而渲染對應的內部信息,還須要添加Link組件以進行跳轉
<font face = "楷體">以後在router.js中對應調用該組件的Route組件中,刪除component屬性,添加render屬性進行頁面渲染,render屬性內應是一個函數,返回Main組件(內部帶有Route屬性以進行路由渲染)
注意點:
<font face = "楷體">調用Main組件的Route不能添加exact屬性,由於若是添加exact屬性,當點擊內部組件路由到對應url時因爲與外部Route的path不是徹底匹配,故將不會顯示
<font face = "楷體">調用Main組件的Route內部render函數若是添加()=>{}則須要在函數體內寫return,由於{}表示函數體,內部的函數將被執行,返回組件須要寫return;若是不添加大括號則直接寫返回的組件便可,ES6箭頭函數默認箭頭後面的內容是return的
<font face = "楷體">調用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;
<font face="楷體">添加react-router-dom的Switch組件包裹Route組件用於設置自上自下只匹配一個路由
<font face="楷體">添加沒有path屬性的Route組件放置Switch組件內部的最後位置,做爲默認路由
NoMath.js
import React from 'react'; class NoMatch extends React.Component{ render(){ return( <div> 404 Not Found </div> ); } } export default NoMatch;
<font face="楷體">因爲用戶訪問項目時輸入url須要有對應的輸出,而做爲整個文件輸出時,一共有三種狀況:登陸、詳情頁、首頁。故須要編寫項目的入口文件router.js並在index.js中引入
<font face="楷體">router文件中定義使用路由的方式爲HashRouter
<font face="楷體">因爲咱們要訪問完整路由時有登陸頁面、詳情頁面和首頁,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;
<font face="楷體">將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;
<font face="楷體">有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;
<font face="楷體">在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'
<font face="楷體">primary表示主按鈕
<font face="楷體">不寫type表示默認樣式按鈕
<font face="楷體">dashed表示虛線按鈕
<font face="楷體">danger表示危險按鈕
<font face="楷體">plus表示加號
<font face="楷體">danger表示危險按鈕
<font face="楷體">delete表示刪除
<font face="楷體">search表示搜索
<font face="楷體">download表示下載
<font face="楷體">circle表示圓形
<font face="楷體">small小按鈕組件
<font face="楷體">default默認大小按鈕組件
<font face="楷體">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; } }
<font face="楷體">效果圖
補充知識點
當Route頁面內部信息超過當前頁面大小時,會出現滾動條,左側導航欄會跟着一塊兒滾動,導航欄下方爲空包
解決方案
當common.less中的main的定義添加overflow:auto,表示當渲染頁面高度超過當前屏幕時,自動滾動
import {Modal} from 'antd';
<font face="楷體">title屬性做爲標題顯示
<font face="楷體">visible屬性參數爲{true|false},爲true則顯示,爲false則不顯示
<font face="楷體">onCancel屬性值爲一個函數,執行當點擊模態框的×或cancel選項時執行的方法
<font face="楷體">組件的onClick值爲this.handleName(即函數名)時,表示一開始就會自動調用,沒法傳參;當須要傳參時,須要將onClick中的內容變爲箭頭函數,返回代參的調用方法從而實現點擊時執行函數並傳參調用方法
<font face="楷體">傳遞的參數若是想做爲對象的鍵時,須要用[]進行包裹,若是沒包裹直接放置鍵的位置則視爲變量而報錯
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> ); } }
<font face="楷體">效果圖
<font face="楷體">Model組件的visible屬性{true}或{false}實現是否顯示
<font face="楷體">Model組件的okText屬性設置OK選項的顯示內容
<font face="楷體">Model組件的cancelText屬性設置Cancel選項顯示內容
<font face="楷體">利用style屬性值爲{{top:20}}設定距頂部20px
<font face="楷體">利用Model組件的wrapClassName設定樣式名稱
<font face="楷體">實例代碼
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);
<font face="楷體">若是不使用AntD封裝好的方法來或許,常規的react 是先綁定onClick或onChange事件,經過事件裏的方法獲取target目標源,在獲取目標源裏的value值,而後把value值存起來,才能使用
<font face="楷體">AntD 經過getFieldDecorator 屬性,來讀取用戶名及密碼,直接進行使用,且使用以前必須經過Form.create()建立一個對象/表單,以後咱們才能使用getFieldDecorator
<font face="楷體">this.props.form.getFieldDecorator是AntD已經封裝好的,固定語法,要記住
const { getFieldDecorator } = this.props.form;
<font face="楷體" color=#CD2626>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>
<font face="楷體" color=#CD2626>必需要有下面這句話。經過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}`) } }) }
<font face="楷體">效果如圖
<font face="楷體" color=#CD2626>必定要在getFieldDecorator裏聲明valuePropName:'checked',才能默認勾選Checkbox 的按鈕
<FormItem> { getFieldDecorator('remember',{ valuePropName:'checked', initialValue:true })( <Checkbox >記住密碼</Checkbox> ) } <a href="#" style={{float:'right'}}>忘記密碼</a> </FormItem>
<font face="楷體" color=#CD2626>使用prefix={}
<Input prefix={<Icon type="user"/>} placeholder="請輸入用戶名"/> <Input prefix={<Icon type="lock"/>} placeholder="請輸入密碼"/>
<font face="楷體">總體效果如圖:
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);
<font face="楷體">
在設置聯繫地址的範圍最大行數,最小行數時:autoSize={} 裏面要求是一個對象。如const rows = {minRows:2,manRows:6}; autoSize={rows } ;成立。autoSize={minRows:2,manRows:6} 將報錯。
AntD官方文檔描述以下:
<FormItem label="聯繫地址" {...formItemLayout}> { getFieldDecorator('address',{ initialValue:'北京市海淀區奧林匹克公園' })( <TextArea autosize={rowObject} /> ) } </FormItem>
<font face="楷體">效果如圖:
<font face="楷體" color=#CD2626>同時顯示日期和時間 要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>
<font face="楷體">如圖
<font face="楷體">要用本身的接口替換 action="//jsonplaceholder.typicode.com/posts/"裏的內容
<font face="楷體">不須要 getBase64 方法,直從接口返回上傳成功的URL地址,服務端把圖片存到服務器裏,返回前段一個服務器的圖片地址,咱們把圖片地址放在img的src展現
<font face="楷體" color=#CD2626>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>
<font face="楷體" color=#CD2626>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> ); } }
<font face="楷體" color=#CD2626>點擊行內任意一處,均有提示
<font face="楷體" color=#CD2626>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); } }) }); } }
<font face="楷體" color=#CD2626>render函數return內容:
<Card title="Mock-表格分頁" style={{margin:'10px 0'}}> <Table bordered columns = {columns } dataSource={this.state.dataSource2} pagination={this.state.pagination} /> </Card>
<font face="楷體" color=#CD2626>邏輯代碼
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') }) }) } }) }
<font face="楷體" color=#CD2626>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) } }) }); }
<font face="楷體" color=#CD2626>發送ajax時isShowLoading:true,也能夠不寫
axios.ajax({ url:'/table/list', data:{ params:{ page:this.params.page }, //不想有loading時。默認爲true // isShowLoading :false, } }).then((res)=>{ }) }
<font face="楷體" color=#CD2626>發送請求前加載
//發送請求前加載 if(options.data && options.data.isShowLoading !== false){ loading = document.getElementById('ajaxLoading'); //block顯示loading loading.style.display = 'block'; }
<font face="楷體" color=#CD2626>請求成功後關閉loading
//請求成功後關閉loading if(options.data && options.data.isShowLoading !== false){ loading = document.getElementById('ajaxLoading'); //none 關閉loading loading.style.display = 'none'; }
<font face="楷體" color=#CD2626>注意:不想有loading時,要設置ShowLoading:false,默認爲true
axios.ajax({ url:'/table/list', data:{ params:{ page:this.params.page }, //不想有loading時。默認爲true // isShowLoading :false, } }).then((res)=>{ }) }
<font face="楷體" color=#CD2626>點擊行內任意一處,均有提示
<font face="楷體" color=#CD2626>render裏return的標籤內容
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> }
<font face="楷體" color=#CD2626>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> ) } }
<font face="楷體">table標籤裏設置scroll={{scroll={{y:240}}
<font face="楷體">y:y軸、240:y軸長度
<Card title="頭部固定"> <Table bordered columns={columns } dataSource={this.state.dataSource} pagination={false} scroll={{y:240}} /> </Card>
<font face="楷體">table標籤裏設置scroll={{scroll={{x:1050}}
<font face="楷體">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>
<font face="楷體">在標題欄下設置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', }, ]
<font face="楷體">若中間有縫隙,可增長標題種類
<font face="楷體">若中間有過分太窄,修改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> ) }
<font face="楷體">升序/降序功能的實現,加兩個參數sorter和sortOrder
{ title: '年齡', dataIndex: 'age', width:80, //升序 降序 功能實現 加兩個參數 sorter:(a,b)=>{ return a.age-b.age; }, sortOrder:this.state.sortOrder },
<font face="楷體">邏輯代碼
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> ) }
<font face="楷體">邏輯代碼
//刪除操做 handleDelete = (item) =>{ let id = item.id; Modal.confirm({ title:'確認', content:'您確認要刪除磁條數據嗎?', onOK:()=>{ message.success('刪除成功'); this.request(); } }) }
<font face="楷體">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,是一種可內嵌於瀏覽器,所見即所得的文本編輯器
<font face="楷體">react-draft-wysiwyg:文本編輯器插件
<font face="楷體">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);
若是本文對你有幫助得話,給本文點個贊❤️❤️❤️
歡迎你們加入,一塊兒學習前端,共同進步!