基础回顾
函数
不绑定this时一定要遵循这种函数形式(箭头函数)
handleAdd=() => {...}
this.handleClick.bind(this)
handleClick(){...}
还有一种:
constructor(props){
super(props);
this.handleClick=this.handleClick.bind(this)
}
引入子组件到父组件里
import Child from './Child'
return(
<Child name="Jack"></Child>//给子组件传递了name值
)
will mount渲染之前
did mount渲染之后
willReceiveProps在接受值之前
state={...}也可以创建参数 不用非要在constructor里写
暴露配置文件(里的eject)
yarn eject
之后里东西变多了
还有一个scripts文件夹 里面有打包好的脚本
还有config文件夹
里面的webpack. … . 开发的配置
prod 生产的配置
server 本地server开发 构建本地的服务器
在dev加的配置在prod里也要加 两个是同步的
要想用less 前提安装less-loader
webpack文件都是从后往前解析
babel-plugin-import 按需加载
example:
1、 {"libraryName:""antd"}//只会加载里面引入的组件js文件
2、{"libraryName:""antd",style:"css"}//加载组件编译好之后的css文件
3、{"libraryName:""antd",style:true}//加载组件并加载组件下面的less文件模块
//不是css\js会帮你编译成css\js
3.0.2 主版本.次版本.打补丁的版本
public文件夹是build之后代码打包生成的文件夹目录,这里面的内容最终会部署到服务器上面,但实则只是里面的内容,并没有所谓的public这个文件夹,所以引用时去掉public,直接’/’
更改antd主题颜色:看文档API
箭头函数:
(item)=>{}
<=>
function(item){…}
百度接口
网上搜 申请百度AK
jsonp插件(可以跨域,axios不能跨域
URL与URI
/koflance/article/details/79635240
JavaScript encodeURIComponent() 函数
encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
很多时候需要对中文进行编码
控制台 ctrl+p可以搜索文件
控制台里Network/接口/Query String Parameters/callback:
旁边Preview,服务端里就会有一个方法将结果包进来
debugger; //断点测试
less:
嵌套
& 代表的上一层选择器的名字
主页结构定义:
- 页面结构定义
- 目录结构定义
- calc()函数 自动计算长度
- 栅格系统使用(运用固定的格子设计版面布局,antd的
即/components/grid-cn/
一行相加=24格
flex 是 flex-grow、flex-shrink、flex-basis的缩写(flex布局
我们把主结构创建为src/
公共样式 src/style/(less好处是可以嵌套、定义一些变量)
嵌套意思就是:
div{
..
a: {...}
}
可以包含一些里面的样式
不需要
div{}
div a{}
还可以声明变量
定义:@colorA:'red'
div{
color:@colorA
}
引用:
@import " ./../../style/";
color:@colorJ;
components文件夹放公共组件
components
- Footer
- Header
- NavLeft
公共页面src/pages
home
src/
在src/
import Router from ‘./router’
(< Router /> ,(‘root’));
需要有一个页面包含各种页面
App.js
class ....
{
return(
<div>
{this.props.children}//可以加载任何页面
</div>
)
}
router.js
import ....
export default class ....{
render(){
return(
<HashRouter>
<App>
<Route path="/login" component={Login} />
<Route path="/admin" render={()=>
<Admin>
<Switch>
<Route path="/admin/ui/buttons" component={Buttons} />//子路由的嵌套
<Route component={NoMatch} />//没匹配的都会跳转到404
</Switch>
</Admin>
} />//进入admin即可进入主页面
</App>
</HashRouter>
);
}
}
src/config文件夹 放一些变量配置(动态控制显示的菜单根据权限
src/axios文件夹 做一些封装
import JsonP from 'jsonp';
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.message);//失败时
}
})
})
}
}
src/utils
export default{
formateDate(time){
if(!time)return ' ';
let date= new Date(time);
return date.getFullYear()+'-'+(date.getMounth()+1)+'-'+date.getDate()+date.getHours()+':'+getMinutes()+':'+date.getSeconds();
}
}
footer/index.less
.home-warp{
background-color:white;
height:calc(60vh);
display:flex;//flex布局
align-items:center;//垂直方向居中
justify-content:center;//水平方向居中
}
Footer/index.js
/index.less
...略...
components/Header/index.js
import React from 'react'
import { Row,Col } from 'antd';
import Util from '../../utils/utils'
import './';
import axios from '../../axios'
export default class Header extends React.Component{
state={}//要提前声明才能Set
componentWillMount(){
this.setState({
userName:'用户名'
})
setInterval(()=>{
let sysTime = Util.formateDate(new Date().getTime());
this.setState({sysTime})
},1000)//每隔1s
this.getWeatherAPIData();
}
getWeatherAPIData(){
let city='北京';
axios.jsonp({
url:'url'+encodeURIComponent(city)
}).then((res)=>{
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>欢迎,{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>
)
}
}
header/index.less
.header{
background-color:white;
.header-top{
height:60px;
line-height:60px;
padding:0 20px;
text-align:right;
a{
margin-left:40px;
}
}
.breadcrumb{
height:40px;
line-height:40px;
padding:0 20px;
border-top:1px solid #f9c700;
.breadcrumb-title{
text-align:center;
font-size:@fontC;
&:after{
position:absolute;//:after 选择器在被选元素的内容后面插入内容。
content:'';
left:74px;
top:40px;
border-top:9px solid @colorM;
border-left:12px solid transparent;//化为透明后是左下角没了
border-right:12px solid transparent;//右下角没了 形成三角形
}
}
.weather{
font-size:14px;
text-align:right;
.date{margin-right:10px;}
.weather-img{
img{ height:15px;}
}
.weather-detail{
margin-left:5px;
vertical-align:middle;
}
}
}
}
menuConfig.js
export default [
{
title:'首页',
key:'/admin/home'
},
{ title:'UI',
key:'admin/ui',
children:[
{
title:'按钮',
key:'url',
},
...
]},
...
]
components/NavLeft/index.js
import React from 'react'
import MenuConfig from './../../config/menuConfig'
import { Menu,Icon } from 'antd';
import './'
const SubMenu = Menu.SubMenu;
export default class NavLeft extends React.Component {
componentWillMount(){//递归方式实现菜单渲染
const menuTreeNode= this.renderMenu(MenuConfig);
this.setState({//setstate完就调用render
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}>
<NavLink to={item.key}>
{item.title}
</NavLink>
</Menu.Item>
})
}
render(){
return (
<div>
<div className="logo">
<img src="/assets/" alt="" />
<h1>Imooc MS </h1>//manage system
</div>
<Menu theme="dark">
{this.state.menuTreeNode}
</Menu>
</div>
)
}
}
.index.less
.logo{
display:inline-block;
line-height:90px;
padding-left:20px;
background-color:#001529;
img{
height:35px;
}
h1{
color:#ffffff;
font-size:20px;
margin:0 0 0 10px;
display:inline-block;
vertical-align:middlel;//元素垂直对齐方式
}
}
common.less
@import './';
.container{
display:flex;
.nav-left{
width:15%
background-color:#001529;
color:#ffffff;
min-width:180px;
height:calc(100vh);//100vh:相当于100%
}
.main{
flex:1;
height:calc(100vh);
background-color:@colorL;
}//右侧区域
.content{
position:relative;
padding:20px;
}//内容区域
}
src/index.js
ReactDOM.render(<Admin />,document.getElementByID('root));//以admin为主页面
admin.js
import React from 'react'
import { Row } from 'antd';
import './style/'
import Header from './components/Header'
import Footer from './components/Footer'
import NavLeft from './components/Navleft'
export default class Admin extends React.Component {
render(){
return (
<Row className="container">
<Col span="3" className="nav-left">
<NavLeft />
</Col>
<Col span="21" className="main">
<Header />
<Row className="content">
{this.props.children}//动态实现 嵌套子组件
</Row>//body区域
<Footer />
</Col>
</Row>
)
}
}
主按钮 < Button type="primary>…<…>
danger按钮 type=“danger” 如:删除按钮
type=“dashed” 虚线边框按钮
< Button disabled> 添加 disabled 属性即可让按钮处于不可用状态,同时按钮样式也会改变
图形按钮(文字前面有小图标
<Button icon="plus">+ </Button>
edit 编辑
delete 删除
danger 危险
download 下载
search 搜索
还可以指定形状
shape="circle" icon="search" 圆形按钮
loading按钮(loading时不能重复点击
loading={true}
作用域要指向React对象用箭头函数
...(){ }需要绑定this指针
按钮组
<Button.Group>
<Button icon="left" style={{marginRight:0}}></Button>
<Button icon="right"></Button>
</Button.Group>
如果两个按钮中间有空隙,应该是原先全局margin-right/left原因,直接强制加上style即可
另一种方式 :加上固定的className="card-wrap"
单选框
handleChange =(e)=>{
this.setState({
size:e.target.value//e为鼠标事件对象 获取事件源对象
});
}
<Radio.Group value={this.state.size} onChange={this.handleChange}>
<Radio value="small/default/large">小/中/大</Radio>
</Radio.Group>
//注意别忘了提前在state里声明 size:default
<Button size={this.state.size}></Button>
弹框
state={
showModal1:false,
showModal2:false,
showModal3:false,
showModal4:false,
}
handleOpen =(type)=> {
this.setState({
[type]:true//即可控制传进来的【type】变量
})
}
<Card title="..." className="card-wrap">
<Button Onclick={() =>this.handleOpen
('showModal1')}>...</Button>
...同理
</Card>
<Modal onCancle={()=>{//点击cancle按钮关闭弹框 同时也作用在叉号上
this.setState({
showModal1:false
})
}}
visible={this.state.showModal1}>
<p>...</p>
</Modal>
自定义页脚弹框
<Modal onCancle={()=>{//点击cancle按钮关闭弹框 同时也作用在叉号上
this.setState({
showModal2:false
})
}}
okText="好的"//确定按钮的文本
cancelText="算了"//退出按钮文本
visible={this.state.showModal2}>
<p>...</p>
</Modal>
顶部20px弹框
<Modal onCancle={()=>{//点击cancle按钮关闭弹框 同时也作用在叉号上
this.setState({
showModal3:false
})
}}
style={{top:20}}//前提要修改less
visible={this.state.showModal3}>
<p>...</p>
</Modal>
top=20px的弹框需要修改UI框架less,也就是在ui/ui.less里贴一份修改的代码
水平垂直居中的弹框
<Modal onCancle={()=>{//点击cancle按钮关闭弹框 同时也作用在叉号上
this.setState({
showModal4:false
})
}}
wrapClassName="vertical-center-modal"//自定义的classname
visible={this.state.showModal4}>
<p>...</p>
</Modal>
ui.less
.vertical-center-modal{
text-align:center;
white-space:nowrap;//规定段落中的文本不进行换行
}
信息确认框:
confirm
info//信息
success
warning
<Card title="..." className="card-wrap">
<Button Onclick={() =>this.handleConfirm
('confirm')}>...</Button>
...同理
</Card>
handleConfirm= (type)=>{
//=a['b']
Modal[type]({//调用的modal方法
title:'标题',
context:'文本',
onOk(){//点击确定执行。。
},
onCancel(){
}//点击取消执行。。
})
}
Spin组件(Loading)
export default class loadings extends React.Component{
render(){
const icon = <Icon type="loading" />
return(
<div>
<Card title="spin" className="card-wrap">
<Spin size="large/small/default" />
<Spin indicator={icon} /> //indicator指定用啥图标作为loading图标发现不能旋转
</Card>
<Card title="alert" className="card-wrap">
<Spin>
<Alert message="react" description="welcome" type="info" />
</Spin>
<Spin tip="加载中。。。">
<Alert message="react" description="welcome" type="info" />
</Spin>
</Card>
</div>
)
}
}
Notification通知提醒框
openNotification =(type,direction) =>{
if(direction){
notification.config({
placement:direction
})
}
notification[type]({
message:'标题',
description:'...'
});
}
<Button onClick={()=>this.openNotification('success','topLeft')}>success</Button>
<Button onClick={()=>this.openNotification('info',.右上..)}>info</Button>
<Button onClick={()=>this.openNotification('warining',.左下..)}>warning</Button>
<Button onClick={()=>this.openNotification('error',..右下.)}>error</Button>
placement设置弹出位置的方向
Tab标签页
const TabPane =Tabs.TabPane;
callback =(key) =>{
...
}
<Card >
<Tabs defaultActiveKey="1" onChange={this.callback}>//默认面板 onChange切换面板的回调 比如打印是什么面板
<TabPane tab={<span><Icon type="plus" /> Tab 1</span>} key="1">Content of Tab Pane 1</TabPane>
<TabPane tab="Tab 2" key="2" disabled>Content of Tab Pane 2</TabPane> //disabled 不可点的
<TabPane tab="Tab 3" key="3">Content of Tab Pane 3</TabPane>
</Tabs>
</Card>
可删可加的标签页
componentWillMount (){
const panes=[
{
title:'Tab 1',
content:'111',
key:'1'
},
{
title:'Tab 2',
content:'222',
key:'2'
},
{
title:'Tab 3',
content:'333',
key:'3'
}
]
this.setState({
panes:panes可以简写为panes,
activeKey:panes[0].key
})
}
newTabIndex=0;
//activekey可以是字符串+变量 总之是要一个独一无二得值
onChange =(activeKey) =>{
this.setState({
activeKey //把激活的key保存起来
})
}
onEdit = (targetKey, action) => {
this[action](targetKey);
}
add = () => {
const panes = this.state.panes;
const activeKey = `newTab${this.newTabIndex++}`;
panes.push({ title: 'New Tab', content: 'New Tab Pane', key: activeKey });
this.setState({ panes, activeKey });
}
remove = (targetKey) => {
let activeKey = this.state.activeKey;
let lastIndex;
this.state.panes.forEach((pane, i) => {
if (pane.key === targetKey) {
lastIndex = i - 1;
}
});
//filter是一个过滤器 (需要保留的内容)
const panes = this.state.panes.filter(pane => pane.key !== targetKey);
if (panes.length && activeKey === targetKey) {
//要删除的和打开的是一个标签页则删除后打开上一个标签页
if (lastIndex >= 0) {
activeKey = panes[lastIndex].key;
} else {
activeKey = panes[0].key;
}
}
this.setState({ panes, activeKey });
}
<Card >
<Tabs defaultActiveKey="1"
type="editable-card" //可编辑的卡片 带加号
activeKey={this.state.activeKey}//当onchange完了之后指定当前激活的是哪个key
onChange={this.onChange}
onEdit={this.onEdit}
>//默认面板 onChange切换面板的回调 比如打印是什么面板
{
this.state.panes.map((panes)=>{
return <TabPane
tab={panel.title}
key={panel.key}
/>//return会返回一个新值 panes数组改变类型
})
}
</Tabs>
</Card>
defaultkey和activekey同时存在时先采用activekey
图片画廊
state ={ visible:false }
render(){
const imgs =[ //二维数组 用map遍历会返回一个新的数组 而不会改变原有对象
['..','..','..',..],
['..','..','..',..],
['..','..','..',..]
]
//二维数组两个map 这样写代码十分冗余
const igmList =imgs.map((list)=> {
return list.map((item)=>
return <Card>
</Card>
})
})
openGallery =(imgSrc) =>{
this.setState({
visible:true,
currentImg:'/gallery/'+imgSrc
})
}
const igmList = imgs.map((list) => list.map((item)=>
<Card //public/gallery/...
cover={<img src={'/gallery/'+item} onClick={()=>this.openGallery} />}
style={{marginBottom:10}} //每行间隔
>
<Card.Meta //带图片的卡片 加上标题描述
title="title"
description="..."
/>·
</Card>
))
}
return(
<div>
<Row gutter={10}>//md总和不得超过24 md 列数
//gutter是每列的间隔
<Col md="5"/{5} >
{imgList[0]}//得到第一行
</Col>
<Col md="5"/{5} >
{imgList[1]}//得到第二行
</Col>
</Row>
<Modal
width={...}
height={...}
title="图片画廊"
visible={this.state.visible}
onCancle={()=>{
this.setState({
visible:false
})
}}
footer={null} //不显示 OK CANCEL
>
<img src={this.state.currentImg} alt="" style={{width:'100%'}}/>
</Modal>
</div>
);
轮播图(分图片轮播、文字背景轮播
ui.less
.ant-carousel .slick-slide {
text-align:center;
height:160px;
line-height:160px;
background:#364d79;
overflow:hidden;
} //不设定可能会显示不出来
.slider-wrap .ant-carousel .slick-slide {
height:240px!important;//设置最高优先级
}
{
render(){
return(
<div>
<Card title="文字背景轮播" className="card-wrap">
<Carousel autoplay> //注意要引入less autoplay 自动轮播
<div><h3>1</h3></div>
<div>2</div>
<div>3</div>
</Carousel>
</Card>
<Card title="图片背景轮播" className="slider-wrap">
<Carousel autoplay > //注意要引入less autoplay 自动轮播
<div> //把图片放到public/carousel-img文件夹
<img src="/carousel-img/" alt="" />
</div>
<div>
<img src="/carousel-img/" alt="" /> </div>
<div>
<img src="/carousel-img/" alt="" /></div>
</Carousel>
</Card>
</div>
)
}
}