项目实战二:共享单车后台

基础回顾

函数
不绑定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里写

暴露配置文件(package.json里的eject)
yarn eject
之后package.json里东西变多了
还有一个scripts文件夹 里面有打包好的脚本
还有config文件夹
里面的webpack. … .dev.js 开发的配置
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
https://blog.csdn.net/koflance/article/details/79635240
JavaScript encodeURIComponent() 函数
encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
很多时候需要对中文进行编码

控制台 ctrl+p可以搜索文件
控制台里Network/接口/Query String Parameters/callback:
旁边Preview,服务端里就会有一个方法将结果包进来

debugger; //断点测试

less:
嵌套
& 代表的上一层选择器的名字


主页结构定义:

  • 页面结构定义
  • 目录结构定义
  • calc()函数 自动计算长度
  • 栅格系统使用(运用固定的格子设计版面布局,antd的
    即https://ant.design/components/grid-cn/
    一行相加=24格

flex 是 flex-grow、flex-shrink、flex-basis的缩写(flex布局

我们把主结构创建为src/admin.js
公共样式 src/style/common.less(less好处是可以嵌套、定义一些变量)

嵌套意思就是:
div{
..
a: {...}
}
可以包含一些里面的样式
不需要
div{}
div a{}

还可以声明变量
定义:@colorA:'red'
div{
	color:@colorA
}

引用:
@import  " ./../../style/default.less";
color:@colorJ;

components文件夹放公共组件
components

  • Footer
  • Header
  • NavLeft

公共页面src/pages
home

  • index.js
  • index.less

src/router.js
在src/index.js
import Router from ‘./router’
ReactDOM.render(< Router /> ,document.getElementById(‘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文件夹 放一些变量配置(动态控制显示的菜单根据权限

  • menuConfig.js

src/axios文件夹 做一些封装

  • index.js
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
utils.js

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 './index.less';
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 './index.less'
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/logo-ant.svg" 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 './default.less';
.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/common.less'
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为鼠标事件对象 e.target获取事件源对象
	});
}
<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=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/1.jpg" alt="" />
					</div>
					<div>
					<img src="/carousel-img/2.jpg" alt="" />						   		   </div>    
					<div>
					<img src="/carousel-img/3.jpg" alt="" /></div>
				</Carousel>
				</Card>
			</div>
		)
	}
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值