共享单车—— React后台管理系统开发手记:UI菜单各个组件使用(Andt UI组件)
Posted 宁静致远
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了共享单车—— React后台管理系统开发手记:UI菜单各个组件使用(Andt UI组件)相关的知识,希望对你有一定的参考价值。
前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录。最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star。
一、按钮Button
- pages->ui->button.js:对应路由/admin/ui/buttons
import React from \'react\'; import {Card, Button, Radio} from \'antd\' import \'./ui.less\' class Buttons extends React.Component{ state = { loading: true, size: \'default\' } handleCloseLoading = () =>{ this.setState({ loading: false }) } handleChange = (e) => { this.setState({ size: e.target.value }) } render(){ return( <div> <Card title="基础按钮" className="card-wrap"> <Button type="primary">提交</Button> <Button>普通</Button> <Button type="dashed">非重要功能</Button> <Button type="danger">删除</Button> <Button disabled>禁用</Button> </Card> <Card title="图形按钮" className="card-wrap"> <Button icon="plus">创建</Button> <Button icon="edit">编辑</Button> <Button icon="delete">删除</Button> <Button shape="circle" icon="search"></Button> <Button type="primary" icon="search">搜索</Button> <Button type="primary" icon="download">下载</Button> </Card> <Card title="Loading按钮" className="card-wrap"> <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="按钮组" style={{marginBottom:10}}> <Button.Group> <Button type="primary" icon="left">返回</Button> <Button type="primary" icon="right">前进</Button> </Button.Group> </Card> <Card title="按钮尺寸" className="card-wrap"> <Radio.Group size={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> ); } } export default Buttons;
- Button组件
- type属性 :表示按钮类型
- disable属性:表示禁用按钮
- icon属性:表示按钮图标样式 icon图标集合
- shape属性:表示按钮形状(circle表示圆形)
- loading属性:为{true}表示加载中(此时按钮不能点击)
- size属性:表示组件大小
- Button.Group按钮组组件:表示包含的Button组件是一个组
- Card组件
- title属性用于标注卡片上方标题
- Radio组件
- 外部需要用Radio.Group组件包裹
- 通过外部组件对象可以获得内部Radio组件的value值(通过
e.target.value
)
- 坑:Route页面内容超过屏幕时,会出现滚动条,左侧下方出现空白区
- 解决:ui.less中给main添加overflow:auto; 当渲染页面高度超过当前屏幕时,自动滚动
二、弹框Modal
- pages->ui->modals:对应路由/admin/ui/modals
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>欢迎使用柳柳版弹框</p> </Modal> <Modal title="React" visible={this.state.showModal2} okText="好的" cancelText="算了" onCancel={() => { this.setState({ showModal2: false }) }}> <p>欢迎使用柳柳版弹框</p> </Modal> <Modal title="React" style={{top:20}} visible={this.state.showModal3} onCancel={() => { this.setState({ showModal3: false }) }}> <p>欢迎使用柳柳版弹框</p> </Modal> <Modal title="React" wrapClassName="vertical-center-modal" visible={this.state.showModal4} onCancel={() => { this.setState({ showModal4: false }) }}> <p>欢迎使用柳柳版弹框</p> </Modal> </div> ) } }
- Modal组件
- title属性:作为标题显示
- visible属性:参数为
{true|false}
,为true则显示,false则不显示 - onCancel属性:值为一个函数,执行当点击模态框的×或cancel选项时执行的方法
- Model内部填写的内容将作为模态框的正文内容
- Model自定义页脚实现方式
- visible属性:{true}或{false}实现是否显示
- okText属性:设置OK选项的显示内容
- cancelText属性:设置Cancel选项显示内容
- Model顶部20px弹框实现方式
- style属性:值为{{top:20}}设定距顶部20px
- Model水平居中实现方式
- 利用Model组件的wrapClassName设定样式名称
- React监听事件方法时手动绑定this
- 除了使用xxx.bind(this)外,直接使用箭头函数定义方法会自动绑定this到当前React实例上
handleConfirm = (type) => { Modal[type]({ title: \'确认?\', content:\'你确认你学会了React了吗?\', onOk() { console.log(\'ok\') }, onCancel() { console.log(\'Cancel\') } }) }
- React事件调用方法并传参
- onClick事件内容若为this.handleConfirm(\'confirm\')表示一开始就会自动调用,无法传参;
- 当需要传参时,需要将onClick中的内容变为:箭头函数返回代参的调用方法,从而实现点击时执行函数并传参调用方法
onClick={() => this.handleConfirm(\'confirm\')}
-
传递的参数如果想作为对象的键时,需要用
[]
进行包裹,如果没包裹直接放置键的位置则视为变量而报错
三、加载中Spin
- pages->ui->loadings:对应路由/admin/ui/loadings
import React from \'react\'; import {Card, Spin, Icon, Alert} from \'antd\' import \'./ui.less\' export default class Loadings extends React.Component{ render() { const icon = <Icon type="loading" style={{fontSize:24}}/> return ( <div> <Card title="Spin用法" className="card-wrap"> <Spin size="small"/> <Spin style={{margin:\'0 10px\'}}/> <Spin size="large"/> <Spin indicator={icon} style={{marginLeft:10}}/> </Card> <Card title="内容遮罩" className="card-wrap"> <Alert message="React" description="欢迎使用柳柳版信息框" type="info" /> <Spin> <Alert message="React" description="欢迎使用柳柳版警告框" type="warning" /> </Spin> <Spin tip="加载中"> <Alert message="React" description="欢迎使用柳柳版警告框" type="warning" /> </Spin> <Spin indicator={icon}> <Alert message="React" description="欢迎使用柳柳版警告框" type="warning" /> </Spin> </Card> </div> ) } }
- Spin组件
- indicator属性:加载指定的图标indicator={icon}
const icon = <Icon type="loading"/>; <Spin indicator={icon}/>
- size属性:设置大小
- tip属性:当作为包裹元素时,可以自定义描述文案
tip:"加载中"
- style={{fontSize:24}} 调整显示图标的大小
- 样式注意
- style={{marginLeft:10}},marginLeft:xx ,xx是数字可以省略px
- 如果是style={{margin:\'0 10px\'}},xx 是字符串的情况,则不能省略px
- Alert组件
- type属性:表示提示的样式,有四种选择 success、info、warning、error
- message属性:表示提示内容(设置标题信息)
- description属性:表示提示的辅助性文字介绍(设置具体描述的内容)
- 任意组件蒙板加载效果
- <Spin>组件内嵌套自闭合组件(如<Alert />)
四、通知提醒Notification
- pages->ui->notice.js:对应路由/admin/ui/notification
import React from \'react\'; import {Card, Button, notification} from \'antd\' import \'./ui.less\' export default class Notice extends React.Component{ openNotification = (type, direction) => { if(direction){ notification.config({ placement: direction }) } notification[type]({ message:\'发工资了\', description:\'上个月考勤22天,迟到12天,实发工资2500,请笑纳\' }) } render(){ return ( <div> <Card title="通知提醒" className="card-wrap"> <Button type="primary" onClick={() => this.openNotification(\'success\')}>Success</Button> <Button type="primary" onClick={() => this.openNotification(\'info\')}>Info</Button> <Button type="primary" onClick={() => this.openNotification(\'warning\')}>Warning</Button> <Button type="primary" onClick={() => this.openNotification(\'error\')}>Error</Button> </Card> <Card title="自定义方向位" className="card-wrap"> <Button type="primary" onClick={() => this.openNotification(\'success\',\'topLeft\')}>Success</Button> <Button type="primary" onClick={() => this.openNotification(\'info\',\'topRight\')}>Info</Button> <Button type="primary" onClick={() => this.openNotification(\'warning\',\'bottomLeft\')}>Warning</Button> <Button type="primary" onClick={() => this.openNotification(\'error\',\'bottomRight\')}>Error</Button> </Card> </div> ) } }
- Notification组件Api
- notification.success(config)
- notification.error(config)
- notification.info(config)
- notification.warning(config)
- notification.warn(config)
- Notification组件Config
- message参数:表示通知提醒标题
- description参数:表示通知提醒内容
- placement参数:表示弹出位置(可选)
- Notification全局配置方法
notification.config(options)
- 在调用前提前配置,全局一次生效
openNotification = (type, direction) => { if(direction){ notification.config({ placement: direction }) } notification[type]({ message:\'发工资了\', description:\'上个月考勤22天,迟到12天,实发工资2500,请笑纳\' }) }
五、全局提示框Message
- pages->ui->messages:对应路由/admin/ui/messages
import React from \'react\'; import {Card, Button, message} from \'antd\' import \'./ui.less\' export default class Messages extends React.Component{ showMessage = (type) => { message[type]("恭喜你,React课程晋级成功"); } render(){ return ( <div> <Card title="全局提示框" className="card-wrap"> <Button type="primary" onClick={() => this.showMessage(\'success\')}>Success</Button> <Button type="primary" onClick={() => this.showMessage(\'info\')}>Info</Button> <Button type="primary" onClick={() => this.showMessage(\'warning\')}>Warning</Button> <Button type="primary" onClick={() => this.showMessage(\'loading\')}>Loading</Button> </Card> </div> ) } }
- Message组件Api
- message.success(content, [duration], onClose)
- message.error(content, [duration], onClose)
- message.info(content, [duration], onClose)
- message.warning(content, [duration], onClose)
- message.loading(content, [duration], onClose)
- Message组件Config
- content属性:表示提示内容
- duration属性:表示自动关闭的延时,单位秒(设为 0 时不自动关闭)
- onClose方法:关闭时触发的回调函数
六、页签Tab
- pages->ui->tabs.js:对应路由/admin/ui/tabs
import React from \'react\'; import {Card, Tabs, message, Icon} from \'antd\' import \'./ui.less\' const TabPane = Tabs.TabPane; export default class Tab extends React.Component{ handleCallBack = (key) => { message.info("Hi,您选择了页签:"+key) } componentWillMount(){ this.newTabIndex = 0; const panes = [ { title: \'Tab 1\', content: \'欢迎使用柳柳版页签\', key: \'1\' }, { title: \'Tab 2\', content: \'欢迎使用柳柳版页签\', key: \'2\' }, { title: \'Tab 3\', content: \'欢迎使用柳柳版页签\', key: \'3\' } ] this.setState({ panes, activeKey: panes[0].key }) } onChange = (activeKey) => { this.setState({ activeKey }) } //onEdit、add、remove直接从官网复制过来即可 onEdit = (targetKey, action) => { this[action](targetKey); } add = () => { const panes = this.state.panes; const activeKey = `newTab${this.newTabIndex++}`; panes.push({ title: activeKey, content: \'Content of new Tab\', key: activeKey }); this.setState({ panes, activeKey }); } //activeKey:当前激活的key, targetKey:当前删除的Key remove = (targetKey) => { let activeKey = this.state.activeKey; let lastIndex; this.state.panes.forEach((pane, i) => { if (pane.key === targetKey) { lastIndex = i - 1; } }); const panes = this.state.panes.filter(pane => pane.key !== targetKey); if (lastIndex >= 0 && activeKey === targetKey) { activeKey = panes[lastIndex].key; } this.setState({ panes, activeKey }); } render() { return ( <div> <Card title="Tab页签" className="card-wrap"> <Tabs defaultActiveKey="1" onChange={this.handleCallBack}> <TabPane tab="Tab 1" key="1">欢迎使用柳柳版页签</TabPane> <TabPane tab="Tab 2" key="2" disabled>欢迎使用柳柳版页签</TabPane> <TabPane tab="Tab 3" key="3">欢迎使用柳柳版页签</TabPane> </Tabs> </Card> <Card title="Tab带图的页签" className="card-wrap"> <Tabs defaultActiveKey="1" onChange={this.handleCallBack}> <TabPane tab={<span><Icon type="plus" />增加</span>} key="1">欢迎使用柳柳版页签</TabPane> <TabPane tab={<span><Icon type="edit" />编辑</span>} key="2">欢迎使用柳柳版页签</TabPane> <TabPane tab={<span><Icon type="delete" />删除</span>} key="3">欢迎使用柳柳版页签</TabPane> </Tabs> </Card> <Card title="Tab动态的页签" className="card-wrap"> <Tabs onChange={this.onChange} activeKey={this.state.activeKey} type="editable-card" onEdit={this.onEdit} > { this.state.panes.map((panel) => { return <TabPane tab = {panel.title} key = {panel.key} >{panel.content}</TabPane> }) } </Tabs> </Card> </div> ) } }
- Tabs组件
- type属性:页签的基本样式
- activeKey属性:当前激活页签的key
- defaultActiveKey属性:初始化选中面板的 key,如果没有设置 activeKey
- hideAdd属性:是否隐藏加号图标,在
type="editable-card"
时有效 - onChange方法:切换面板的回调
- onEdit方法:新增和删除页签的回调,在
type="editable-card"
时有效
-
Tabs.TabPane
- 页签面板:使用Tabs组件时必须先初始化TabPane
const TabPane = Tabs.TabPane;
-
tab属性:选项卡头显示文字
-
key属性:对应activeKey
-
forceRender属性:被隐藏时是否渲染 DOM 结构
- Tabs带图页签
- 指定图标:用Icon组件
- 注意:JSX语法,{}根对象中不能直接同时套用<Icon />和文字,外层必须包裹一个根元素<span></span>
tab={<span><Icon type="plus" />增加</span>
-
Tabs动态页签
-
JS变量循环显示:定义生命周期方法componentWillMount执行
-
type="editable-card":
指定样式为可编辑的卡片样式 -
onChange事件:设置页签改变时调用方法,设置激活的key
- onEdit、add、remove方法:直接从官网复制过来即可
七、画廊gallery
- pages->ui->gallery:对应路由/admin/ui/gallery
import React from \'react\'; import {Card, Row, Col, Modal} from \'antd\' import \'./ui.less\' export default class Gallery extends React.Component{ state = { visible: false } openGallery = (imgSrc) => { this.setState({ visible: true, currentImg: \'/gallery/\'+imgSrc }) } render(){ const imgs = [ [\'1.png\',\'2.png\',\'3.png\',\'4.png\',\'5.png\'], [\'6.png\',\'7.png\',\'8.png\',\'9.png\',\'10.png\'], [\'11.png\',\'12.png\',\'13.png\',\'14.png\',\'15.png\'], [\'16.png\',\'17.png\',\'18.png\',\'19.png\',\'20.png\'], [\'21.png\',\'22.png\',\'23.png\',\'24.png\',\'25.png\'] ] const imgList = imgs.map((list) => list.map((item) => <Card style={{marginBottom:10}} cover={<img src={\'/gallery/\'+item} onClick={() => this.openGallery(item)}/>} > <Card.Meta title="React Admin" description="66 Elena gallery" /> </Card> )) return ( <div className="card-wrap"> <Row gutter={10}> <Col md={5}> {imgList[0]} </Col> <Col md={5}> {imgList[1]} </Col> <Col md={5}> {imgList[2]} </Col> <Col md={5}> {imgList[3]} </Col> <Col md={4}> {imgList[4]} </Col> </Row> <Modal width={300} height={500} visible={this.state.visible} title="图片画廊" onCancel={() => { this.setState({ visible: false }) }} footer={null} > {<img src={this.state.currentImg} style={{width:\'100%\'}}/>} </Modal> </div> ) } }
- Card组件
- cover属性:卡片封面
-
Card.Meta
- 使Card组件支持更灵活的内容:灵活内容
- title属性:表示标题内容
- description属性:表示描述内容
- 栅格布局区块间隔
- 关键:
Row
的gutter
属性,常使用(16+8n)px作为左右间隙(n为自然数) - Col 使用样式设置间隙:style={{ marginBottom: 10}}
<Card style={{marginBottom:10}} cover={<img src={\'/gallery/\'+item} onClick={() => this.openGallery(item)}/>} > <Card.Meta title="React Admin" description="66 Elena gallery" /> </Card>
-
点击查看大图
-
onClick点击事件:使用箭头函数,传递 item
-
关闭底部按钮:footer={null}
-
设置Modal宽高:width={300} height={500}
-
控制Modal显示:设置
visible
,onCancel
八、轮播Carousel
- pages->ui->carousel:对应路由/admin/ui/carousel
import React from \'react\'; import {Card, Carousel} from \'antd\' import \'./ui.less\' export default class Carousels extends React.Component{ render(){ return ( <div> <Card title="文字背景轮播" className="card-wrap"> <Carousel autoplay effect="fade"> <div><h3>Ant Motion Banner - React</h3></div> <div><h3>Ant Motion Banner - Vue</h3></div> <div><h3>Ant Motion Banner - Angular</h3></div> </Carousel> </Card> <Card title="图片轮播" className="card-wrap slider-wrap"> <Carousel autoplay> <div><img src="/carousel-img/carousel-1.jpg"/></div> <div><img src="/carousel-img/carousel-2.jpg"/></div> <div><img src="/carousel-img/carousel-3.jpg"/></div> </Carousel> </Card> </div> ) } }
- Carousel组件
- vertical属性:表示是否垂直显示
- dots属性:表示是否显示面板指示点
- autoplay属性:表示是否自动切换
- easing属性:表示动态效果
- effect属性:表示动画效果函数
- beforeChange:切换面板的回调
- afterChange:切换面板的回调
- 图片轮播
- autoplay:设置自动切换
- effect="fade":指定淡入淡出动画,不指定默认为scalex动画
- 轮播组件开始并没有显示:需要引入官网demo的样式或自定义样式
/* Carousel For demo */ .ant-carousel .slick-slide { text-align: center; height: 160px; line-height: 160px; background: #364d79; overflow: hidden; } .ant-carousel .slick-slide h3 { color: rgba(246, 250, 33, 0.966); }
- 自定义样式:修改图片的默认高度,!important设置优先级
// 图片轮播 .slider-wrap .ant-carousel .slick-slide { height: 240px!important; }
九、路由及样式
- router.js:配置Admin组件的子路由
import React from \'react\' import {HashRouter, Route, Switch} from \'react-router-dom\' import App from \'./App\' import Login from \'./pages/Login\' import NoMatch from \'./pages/NoMatch\' import Admin from \'./admin\' import Home from \'./pages/Home\' import Buttons from \'./pages/ui/buttons\' import Modals from \'./pages/ui/modals\' import Loadings from \'./pages/ui/loadings\' import Notice from \'./pages/ui/notice\' import Messages from \'./pages/ui/messages\' import Tabs from \'./pages/ui/tabs\' import Gallery from \'./pages/ui/gallery\' import Carousel from \'./pages/ui/carousel\' export default class IRouter extends React.Component{ render() { return ( <HashRouter> <App> <Route path="/login" component={Login}></Route> <Route path="/admin" render={() => <Admin> <Switch> <Route path="/admin/home" component={Home}></Route> <Route path="/admin/ui/buttons" component={Buttons}></Route> <Route path="/admin/ui/modals" component={Modals}></Route> <Route path="/admin/ui/loadings" component={Loadings}></Route> <Route path="/admin/ui/notification" component={Notice}></Route> <Route path="/admin/ui/messages" component={Messages}></Route> <Route path="/admin/ui/tabs" component={Tabs}></Route> <Route path="/admin/ui/gallery" component={Gallery}></Route> <Route path="/admin/ui/carousel" component={Carousel}></Route> <Route component={NoMatch}></Route> </Switch> </Admin> }></Route> <Route path="/order/detail" component={Login}></Route> </App> </HashRouter> ) } }
- pages->ui->ui.less:设置ui目录下所有组件的样式
.card-wrap{ margin-bottom: 10px; button{ margin-right: 10px; } } /* modals */ /* use css to set position of modal */ .vertical-center-modal { text-align: center; white-space: nowrap; } .vertical-center-modal:before { content: \'\'; display: inline-block; height: 100%; vertical-align: middle; width: 0; } .vertical-center-modal .ant-modal { display: inline-block; vertical-align: middle; top: 0; text-align: left; } /* Carousel For demo */ .ant-carousel .slick-slide { text-align: center; height: 160px; line-height: 160px; background: #364d79; overflow: hidden; } .ant-carousel .slick-slide h3 { color: rgba(246, 250, 33, 0.966); } // 图片轮播 .slider-wrap .ant-carousel .slick-slide { height: 240px!important; }
注:项目来自慕课网
以上是关于共享单车—— React后台管理系统开发手记:UI菜单各个组件使用(Andt UI组件)的主要内容,如果未能解决你的问题,请参考以下文章
共享单车—— React后台管理系统开发手记:Redux集成开发
共享单车—— React后台管理系统开发手记:城市管理和订单管理
共享单车—— React后台管理系统开发手记:AntD Form基础组件
共享单车—— React后台管理系统开发手记:主页面架构设计