1. 程式人生 > >【共享單車】—— React後臺管理系統開發手記:主頁面架構設計

【共享單車】—— React後臺管理系統開發手記:主頁面架構設計

前言:以下內容基於React全家桶+AntD實戰課程的學習實踐過程記錄。最終成果github地址:https://github.com/66Web/react-antd-manager,歡迎star。


 一、頁面結構定義

  • 左側導航欄,右側頁面結構
  • 右側顯示內容分別分為上Header、中Content和下Footer部分

二、目錄結構定義

  • src->admin.js:專案主結構程式碼(index.js中替換App.js掛載到根節點)
  • src->common.js:專案公共結構程式碼(類似admin.js,負責路由詳情頁展示)
  • src->components
    目錄:專案公共元件
  • components->Footer、Header、NavLeft目錄:index.js+index.less
  • src->pages目錄:專案路由頁面元件
  • src->style目錄:全域性樣式common.less
  • src->config目錄:選單配置、專案其它變數配置
  • src->axios目錄:執行axios請求檔案 index.js
  • src->utils目錄:專案工具方法
  • src->resource->assets目錄:專案圖片資源(public->assets目錄供打包後使用)
  • src->resource->gellary目錄:畫廊圖片資源(public->gellary目錄供打包後使用)
  • src->resource->carousel-img目錄:輪播圖片資源(public->carousel-img目錄供打包後使用)

三、柵格系統使用

  • AntD柵格系統一共24列(BootStrap一共12列)
  • AntD中行用Row元件,列使用Col元件,列存在span屬性(span={長度值}的方式寫入span屬性),同一Row下的Col span總和為24
    import { Row,Col } from 'antd'
    
    <Row>
         <Col span={4}>
              Left
         </Col>
         <Col span={20}>
              Right
         </Col>
    </Row>

四、calc計算方法使用

  • calc()是less中動態計算長度值
  • calc(四則運算):運算子前後都需要保留一個空格
    width: calc(100% - 10px)//表示寬度屬性是整個佈局的100%減去50px的長度
  • calc(100vh):vh的含義相當於1%,100vh即是100%

五、關於less

  • less是預編譯器
  • 用法一:樣式巢狀
    //css中
    div{...}
    div a{...}
    
    //less中
    div{
       ...
       a{
          ... 
       }
    }
  • 用法二:宣告變數
    @colorA:'red'
    div{
       color:@colorA
       a{
          color:black 
       }
    } 

六、架構程式碼

  • 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 './style/common.less'
    class Admin extends React.Component{
      render(){
          return(
              <Row className="container">
                  <Col span={4} className="nav-left">
                      <NavLeft/>
                  </Col>
                  <Col span={20} className="main">
                      <Header/>
                      <Row className="content">
                          content
                      </Row>
                      <Footer/>
                  </Col>
              </Row>
          );
      }
    }
    export default Admin;
  • style->common.less

    .container{
      display: flex;
      .nav-left{
          width: 15%;
          min-width: 180px;
          height: calc(100vh);
          background-color: red;
      }
      .main{
          flex: 1;
          height: calc(100vh);
      }
      .content{
          position: relative;
          padding: 20px;
      }
    }
  • components->Header/Footer/NavLeft->index.js

    import React from 'react';
    class Header extends React.Component{
      render(){
          return(
              <div>Header</div>
          )
      }
    }
    export default Header;

七、導航欄內容

  • 在config下編寫manuConfig.js返回導航欄內容title和key(AntD 路由地址)
    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:'/admin/order/detail'
                },
                {
                    title:'結束訂單',
                    key:'/admin/order/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;  
  • 渲染選單(Menu)
  1. AntD Menu元件:官方文件
  2. 更改主題色:theme屬性  --  light/dark
    import { Menu, Icon } from 'antd';
    import MenuConfig from './../../config/manuConfig'
    
    const SubMenu = Menu.SubMenu;
    
    <Menu theme="dark">
              <SubMenu key="sub1" title={<span><Icon type="mail" /><span>Navigation One</span></span>}>
                    <Menu.Item key="1">Option 1</Menu.Item>
                    <Menu.Item key="2">Option 2</Menu.Item>
                    <Menu.Item key="3">Option 3</Menu.Item>
                     <Menu.Item key="4">Option 4</Menu.Item>
              </SubMenu>
    </Menu>  
  3. src->NevLeft->index.js :通過遞迴方法迴圈遍歷manuConfig.js內容,渲染選單
    import React from 'react'
    import { Menu, Icon } from 'antd';
    import MenuConfig from './../../config/manuConfig'
    import './index.less'
    
    const SubMenu = Menu.SubMenu;
    
    export default class NavLeft extends React.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>LJQ MS</h1>
                    </div>
                    <Menu theme="dark">
                        {this.state.MenuTreeNode}      
                    </Menu>
                </div>
            )
        }
    }
    

    NevLeft->index.less: 

    .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;
        }
    }
    

八、首頁頭部內容 

  • Axios不支援跨域,只支援Ajax相關get/post/put/delete請求
  • Jsonp支援跨域(所謂跨域就是跨域名,跨埠,跨協議)web頁面上呼叫js檔案時則不受跨域影響(且凡是有src屬性的標籤都具有跨域能力,如<\script> <\img> <\iframe>等)
    yarn add jsonp --save
  • Promise最大好處:將回調函式的非同步呼叫變成鏈式呼叫
  1. 函式返回的Promise物件引數接受一個函式
  2. 引數1為成功的resolve回撥函式
  3. 引數2是失敗的reject回撥函式函式體中返回對應函式呼叫
  4. 呼叫該返回Promise物件的函式的then方法
  5. 引數:以resolve函式傳入的屬性值為引數的函式
  6. 在方法體內寫入回撥成功返回的內容
  • Header->index.js
  1. 利用setInterval函式實時重新整理時間資訊,並呼叫utils包中的formate方法格式化時間戳
  2. 呼叫axios包中的jsonp方法傳送請求並根據promise進行回撥
    import React from 'react'
    import { Row, Col } from 'antd'
    import './index.less'
    import Util from '../../utils/utils'
    import axios from '../../axios';
    
    export default class Header extends React.Component {
        state = {}
        componentWillMount() {
            this.setState({
                userName: '萊茵月牙'
            })
            setInterval(() => {
                let sysTime = Util.formateDate(new Date().getTime());
                this.setState({
                    sysTime
                })
            }, 1000)
            //this.getWeatherAPIDate();由於百度API禁止了服務,故該功能暫時不使用
        }
        getWeatherAPIDate() {
            let city = encodeURIComponent('杭州');
            axios.jsonp({
                url: 'http://api.map.baidu.com/telematics/v3/weather?location='+city+'&output=json&ak=kwQXPVDYPZIYArkpi3rQT7aZHTGTCCB2'
            }).then((res) => {
                if(res.status == 'success'){
                    let data = res.result[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>歡迎,{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-detail">晴轉多雲</span>
                       </Col>
                   </Row>
                </div>
            )
        }
    }
    
  3. utils->utils.js
    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() } }
  4. axios->index.js:使用JsonP模組傳送請求獲得jsonp資料,解決跨域問題
    import JsonP from 'jsonp'
    
    export default class Axios{
         static jsonp(options){
             new Promise((resolve, reject) => {
                 JsonP(options.url,{
                     param:'callback'
                 }, function (err, response) {
                    if(response.status === 'success'){
                        resolve(response);
                    }else{
                        reject(response.message);
                    }
                 })
             })
         }
    }  

九、底部元件開發

  • Footer->index.js
    import React from 'react'
    import './index.less';
    
    export default class Footer extends React.Component {
        render() {
            return (
                <div className="footer">
                    版權所有:柳潔瓊Elena
                </div>
            )
        }
    }  
  • less匯入到其他less中 @import "檔案路徑";注意用@import引入,用分號分割】
  • less使用變數方式:@變數名
  • Footer->index.less
    @import "./../../style/default.less";
    
    .footer{
        height: 100px;
        padding: 40px 0;
        text-align: center;
        color: @colorJ;
    }
    

十、Home頁面實現

  • pages->Home->index.js
    import React from 'react'
    import './index.less'
    
    export default class Home extends React.Component{
        render() {
            return (
                <div className="home-wrap">
                    歡迎學習慕課後臺管理系統課程
                </div>
            )
        }
    }
  • pages->Home->index.less

    @import './../../style/default.less';
    
    .home-wrap{
        background-color: @colorM;
        height: calc(62vh); //內容高度佔右側的62%
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
    }
    

    flex彈性佈局:實現水平垂直居中

    display: flex;
    align-items: center;//前提佈局是flex,align-items表示垂直居中
    justify-content: center;//前提佈局是flex,align-items表示水平居中
    
  • CSS實現箭頭圖示

  1. 注意:當內部元素使用絕對定位,父級需要使用相對定位,否則內部元素則會相對整個文件進行絕對定位

  2. after偽類:元素後新增內容實現箭頭圖示

  3. 實現下三角樣式:設定border-top為指定顏色,左右border為透明色(transparent)

     .breadcrumb{
            height: 40px;
            line-height: 40px;
            padding: 0 20px;
            border-top: 1px solid #f9c700;
            position: relative;   //父級元素相對定位
            .breadcrumb-title{
                font-size: @fontC;
                text-align: center;
                &:after{
                    position: absolute;  //絕對定位
                    content: '';    //設為空用於佔位
                    left: 73px;
                    top: 39px;
                    border-top: 9px solid @colorM;
                    border-left: 12px solid transparent;
                    border-right: 12px solid transparent;
                }
            }  

注:專案來自慕課網