React全家桶+AntD單車後臺管理系統項目筆記

前言

本文基於React共享單車後臺管理系統的開發進行學習記錄,內含React生態系統的總體介紹及相關內容整理,方便本身查閱,也但願對小夥伴有所幫助 css

cmd-markdown-logo

React基本介紹

Facebook開源的一個JavaScript庫html

React結合生態構成的一個MV* 框架node

React特色

  • Declarative(聲明式編碼)
  • Component-Based(組件化編碼)
  • 高效-高效的Dom Diff算法,最小化頁面重繪
  • 單向數據流

生態介紹

Vue生態:react

Vue+Vue-Router+Vuex+Axios+Babel+Webpack
複製代碼

React生態:webpack

React+React-Router-Redux+Axios+Babel+Webpack
複製代碼

React生命週期介紹

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中移除以前馬上被調用

Redux

Redux基本介紹

單項數據流:

從父組件流向子組件,兄弟組件沒法共享數據

state:

React中的狀態,是隻讀對象,不可直接修改

Reducer:

基本函數,用於對state的業務處理

Action:

普通對象,用於描述事件行爲,改變state

Redux安裝

yarn add redux --save
yarn add react-redux --save
複製代碼

Redux集成

建立Action模塊

建立Reducer模塊

建立Store模塊

經過connect方法將React組件和Redux鏈接起來

添加Provider做爲項目的根組件,用於數據的存儲

Redux調試工具安裝

在Chrome中安裝Redux Devtools擴展

yarn add redux-devtools-extension
複製代碼

React腳手架、Yarn介紹

安裝React腳手架

npm install -g create-react-app  //安裝這個腳手架
create-react-app may-app    //初始化一個項目
cd my-app   //切換
npm start   //啓動進入
複製代碼

什麼是Yarn?

Yarn是新一代的包管理工具

爲何使用Yarn?

  • 速度快
  • 安裝版本統一,更安全
  • 更簡潔的輸出
  • 更好的語義化

如何使用Yarn?

yarn init   //初始化一個項目
yarn add    //安裝一個包
yarn remove     //刪除一個包
yarn / yarn install     //安裝依賴包
複製代碼

源碼和課程教程

源碼:github.com/shifengming…

課程地址:coding.imooc.com/class/236.h…

效果圖

項目主頁面

cmd-markdown-logo

AntD的Button頁面

cmd-markdown-logo

Echart圖表頁面

cmd-markdown-logo

城市管理頁面

cmd-markdown-logo

Echart餅形圖頁面

cmd-markdown-logo

UI輪播圖,包含文字輪播和圖片輪播

cmd-markdown-logo

車輛地圖頁面

cmd-markdown-logo

用戶受權頁面

cmd-markdown-logo

高級表格頁面

cmd-markdown-logo

菜單權限頁面

cmd-markdown-logo

員工管理頁面

cmd-markdown-logo

表單頁面

cmd-markdown-logo

訂單詳情頁面

cmd-markdown-logo

搭建運行環境

安裝腳手架

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」屬性或方法
複製代碼

開發過程當中重點問題總結

主頁面架構設計

頁面結構定義

  • 左側導航欄,右側顯示內容
  • 右側顯示內容分別分爲Header、中Content和下Footer部分

目錄結構定義

  • 定義components包,分別加入Footer、Header、NavLeft包,內部加入index.js文件,導入時能夠導入到包的級別,系統會自動尋找包下的index.js文件

  • 定義style包,內部加入common.less文件定義全局樣式

柵格系統使用

  • 柵格系統一共24列
  • Antd中行用Row組件,列使用Col組件,列存在span屬性(span={長度值}的方式寫入span屬性),同一Row下的Col span總和爲24

calc計算方法使用

  • clac()是less中動態計算長度值
  • 任何長度值均可以用clac()函數進行計算
  • calc(100vh):vh的含義至關於1%,100vh便是100%

例子使用:

width:clac(100%-50px)   //表示寬度屬性是整個佈局的100%減去50px的長度
複製代碼

關於less

  • less是預編輯器
  • 在div下有a標籤,想設定a的樣式和div的樣式,在less中能夠在定義時嵌套
//css中
div{...}
div a{...}

//less中
div{
    ...
    a:{
      ...  
    }
}
複製代碼
  • less可使用定義的變量
@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

效果以下:

cmd-markdown-logo

實例代碼首頁頭部內容

利用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

  • 模塊發送請求得到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;
                }
            }
        }
    }
}
複製代碼

效果以下

cmd-markdown-logo

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;
;
}
複製代碼

效果以下:

cmd-markdown-logo

總體頁面效果

cmd-markdown-logo

Router 4.0

React Router4.0基本概念介紹

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>
}
複製代碼
  • 定義:取值:this.props.match.params.number
<Link to={{pathname:'/three/7'}}>Three #7</Link>
複製代碼
  • 同時在to屬性中能夠傳遞一個location對象,如:
{pathname:'/',search:'',hash:'',key:'abc123',state:{}}
複製代碼
  • Switch-選擇符合要求的Route,自上至下找到第一個能夠匹配的內容,則不繼續加載其餘
<Switch>
    <Route path='/admin/ui/buttons' component={Buttons}/>
    <Route path='/admin/ui/models' component={Models}/>
    <Route path='/admin/ui/loading' component={Loading}/>
</Switch>
複製代碼
  • Redirect
路由重定向:<Redirect to="/admin/home">
複製代碼

React Router4.0 Demo介紹

  • 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;
複製代碼

UI菜單各個組件使用(Andt UI組件)

按鈕組件

  • 引入Card組件
import {Card} from 'antd'
複製代碼
  • title屬性用於標註卡片上方標題
<Card title="基礎組件"></Card>
複製代碼

cmd-markdown-logo

Button組件

  • 引入Button組件:
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大按鈕組件

Radio組件

  • 引入Rodio組件:
import {Radio} from 'antd'
複製代碼
  • Radio組件外部須要用Radio.Group組件包裹,並經過外部組件對象能夠得到內部Radio組件的value值(經過e.target.value)

應用實例

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;
  }
}
複製代碼

效果圖

cmd-markdown-logo

補充知識點

當Route頁面內部信息超過當前頁面大小時,會出現滾動條,左側導航欄會跟着一塊兒滾動,導航欄下方爲空包

解決方案

當common.less中的main的定義添加overflow:auto,表示當渲染頁面高度超過當前屏幕時,自動滾動

彈框組件-Modal基本組件

  • 引入Modal:
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>
        );
    }
}
複製代碼

效果圖

cmd-markdown-logo

  • 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);
複製代碼

效驗輸入的信息

  • /getFieldValue是object對象,form的一個方法,用於獲取表單下全部的值
  • validateFields方法是校驗信息是否符合要求,校驗下是一個循環,用()=>
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}`)
            }
        })
    }
複製代碼

效果如圖

cmd-markdown-logo

CheckBox記住密碼

必定要在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="請輸入密碼"/>
複製代碼

總體效果如圖:

cmd-markdown-logo

註冊

源代碼

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);
複製代碼

聯繫地址

在設置聯繫地址的範圍最大行數,最小行數時:autoSize={} 裏面要求是一個對象。如const rows = {minRows:2,manRows:6}; autoSize={rows } ;成立。autoSize={minRows:2,manRows:6} 將報錯。 AntD官方文檔描述以下:

cmd-markdown-logo

<FormItem label="聯繫地址" {...formItemLayout}>
        {
            getFieldDecorator('address',{
                initialValue:'北京市海淀區奧林匹克公園'
            })(
                <TextArea
                    autosize={rowObject}
                />
            )
        }
    </FormItem>
複製代碼

效果如圖:

cmd-markdown-logo

日期顯示

同時顯示日期和時間 要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>
複製代碼

如圖

cmd-markdown-logo

上傳頭像

  • 實際作項目中,與如今有所不一樣

    要用本身的接口替換 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>
        );
    }
}
複製代碼

單選表格效果

cmd-markdown-logo

點擊行內任意一處,均有提示

cmd-markdown-logo

多選表格效果

cmd-markdown-logo

分頁表格效果

cmd-markdown-logo

ajax封裝(get)

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);
                }
            })
        });
    }
}
複製代碼

表格分頁:pagination

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)=>{
            
        })
    }
複製代碼

點擊行內任意位置,均有提示,可進行增刪改查

cmd-markdown-logo
點擊行內任意一處,均有提示
cmd-markdown-logo
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>
   }
複製代碼

render上的邏輯代碼

//獲取每一行裏的 item值
    onRowClick = (record,index) =>{
        //多選時用數組
        let selectKey = [index ];
        Modal.info({
            title:'信息',
            content:`用戶名:${record.userName},用戶愛好:${record.interest}`
        });
        this.setState({
            selectedRowKeys:selectKey,
            //獲取item值 可對單個行進行新增刪除操做。
            selectedRowItem:record
        })
    }
複製代碼

checkBox表格多選刪除

cmd-markdown-logo

//多選刪除事件
    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>
        )
    }
}
複製代碼

頭部固定

cmd-markdown-logo
table標籤裏設置scroll={{scroll={{y:240}}

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',
            },
        ]
複製代碼

若中間有縫隙,可增長標題種類

cmd-markdown-logo
若中間有過分太窄,修改scroll={{x:1050}},修改width之和

年齡排序

cmd-markdown-logo

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
        })
 }
複製代碼

按鈕操做-刪除

cmd-markdown-logo

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樣式:

cmd-markdown-logo

{
   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,是一種可內嵌於瀏覽器,所見即所得的文本編輯器

cmd-markdown-logo

安裝插件

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>
        );
    }
}
複製代碼

城市管理頁面

頁面效果

cmd-markdown-logo

Easy Mock城市管理的數據接口

{
  "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);
複製代碼

componentDidMount()

調用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(){
      ...
  }
複製代碼

cmd-markdown-logo

開通城市-彈框功能

  • 爲以上組件,增長點擊按鈕 彈窗(Modal)功能

[開通城市]按鈕:監聽 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>
複製代碼
  • wrapperComponentRef屬性:

拿到表單中的信息對象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);
複製代碼
相關文章
相關標籤/搜索