主页面架构设计
页面结构定义
- 左侧导航栏,右侧显示内容
- 右侧显示内容分别分为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
}
}
###左侧导航栏内容
在public文件夹下添加assets文件夹,放置logo-ant.svg图片
注意:public文件是build文件是build之后存储内容的文件,通常内部放置静态资源,public下的文件内容在访问时全是通过根目录直接访问文件地址
实例代码首页头部内容
利用jsonp可以解决跨域问题(所谓跨域就是跨域名,跨端口,跨协议)
利用setInterval函数实时刷新时间信息,并调用axios包中的jsonp方法发送请求并根据promise进行回调
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等路由地址】
-
实战代码
-
4.0基本路由功能DEMO实现-配置化【将Route路由提取到一个单独的JS文件中】
配置化实现路由功能
创建Home.js内部写上ul->li->Link导航组件,并在想要显示对应路由内容的区域写上{this.props.children}即会加载调用Home.js组建时内部传递的信息
嵌套路由
如想实现在Main组件中的嵌套路由,需要在Main组件中添加{this.props.children}从而渲染对应的内部信息,还需要添加Link组件以进行跳转
之后在router.js中对应调用该组件的Route组件中,删除component属性,添加render属性进行页面渲染,render属性内应是一个函数,返回Main组件(内部带有Route属性以进行路由渲染)
注意点:
调用Main组件的Route内部render函数如果添加()=>{}则需要在函数体内写return,因为{}表示函数体,内部的函数将被执行,返回组件需要写return;如果不添加大括号则直接写返回的组件即可,ES6箭头函数默认箭头后面的内容是return的
-
获取动态路由的值
在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;
在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}显示其页面的组件
将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}
UI菜单各个组件使用(Andt UI组件)
按钮组件
- 引入Card组件
import {Card} from 'antd'
- title属性用于标注卡片上方标题
<Card title="基础组件"></Card>
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)
补充知识点
当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中的内容变为箭头函数,返回代参的调用方法从而实现点击时执行函数并传参调用方法
传递的参数如果想作为对象的键时,需要用[]进行包裹,如果没包裹直接放置键的位置则视为变量而报错
-
Model自定义页脚实现方式
Model组件的visible属性{true}或{false}实现是否显示
Model组件的okText属性设置OK选项的显示内容
Model组件的cancelText属性设置Cancel选项显示内容
-
Model顶部20px弹框实现方式
利用style属性值为{{top:20}}设定距顶部20px
-
Model水平居中实现方式
利用Model组件的wrapClassName设定样式名称
取出用户名及密码的值
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}`)
}
})
}
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="请输入密码"/>
整体效果如图:
注册
联系地址
在设置联系地址的范围最大行数,最小行数时:autoSize={} 里面要求是一个对象。如const rows = {minRows:2,manRows:6}; autoSize={rows } ;成立。autoSize={minRows:2,manRows:6} 将报错。 AntD官方文档描述如下:
<FormItem label="联系地址" {...formItemLayout}>
{
getFieldDecorator('address',{
initialValue:'北京市海淀区奥林匹克公园'
})(
<TextArea
autosize={rowObject}
/>
)
}
</FormItem>
效果如图:
日期显示
同时显示日期和时间 要showTime 和 format=‘YYYY–MM–DD HH:mm:ss’同时使用
<FormItem label="生日" {...formItemLayout}>
{
getFieldDecorator('birthday',{
initialValue:moment('2018-08-08')
})(
<DatePicker
showTime
format="YYYY-MM-DD HH:mm:ss"
/>
)
}
</FormItem>
如图
上传头像
-
实际做项目中,与现在有所不同
要用自己的接口替换 action="//jsonplaceholder.typicode.com/posts/"里的内容
不需要 getBase64 方法,直从接口返回上传成功的URL地址,服务端把图片存到服务器里,返回前段一个服务器的图片地址,我们把图片地址放在img的src展示
antD上传头像使用.jpg的图片格式
//获取文件格式的字符串
getBase64 = (img, callback)=>{
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
handleChange = (info) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
this.getBase64(info.file.originFileObj, imageUrl => this.setState({
userImg:imageUrl,
loading: false,
}));
}
}
<FormItem label="头像" {...formItemLayout}>
{
getFieldDecorator('userImg',{
})(
<Upload
listType="picture-card"
showUploadList={false}
action="//jsonplaceholder.typicode.com/posts/"
onChange={this.handleChange}
>
{this.state.userImg?<img src={this.state.userImg} />:<Icon type="plus"/>}
</Upload>
)
}
</FormItem>
表格-基础表格
发送请求前加载
//发送请求前加载
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)=>{
})
}
表格-高级表格
头部固定
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',
},
]
若中间有缝隙,可增加标题种类
安装插件
react-draft-wysiwyg:文本编辑器插件
draftjs-to-html:文本转换为html的插件
yarn add react-draft-wysiwyg draftjs-to-html --save
城市管理页面
开通城市-弹框功能
- 为以上组件,增加点击按钮 弹窗(Modal)功能
[开通城市]按钮:监听 onClick 事件,调用this.handleOpenCity()显示弹框
state = {
list:[],
isShowOpenCity:false // 默认不可见
}
// 开通城市
handleOpenCity = ()=>{
this.setState({
isShowOpenCity:true
})
}