先放項目地址:https://github.com/1067011734/balibali
感謝無私的程序員們分享
咱們邊看效果邊看代碼
項目運行出來的效果
先看Home頁面
輪播
css
//src\pages\Home\model\Carousel.js import React, { Component } from 'react' import { Carousel } from 'antd-mobile' import img from '@/images/banner/1.jpg' import img2 from '@/images/banner/2.jpg' import img3 from '@/images/banner/3.jpg' class app extends Component { state = { data: ['1', '2', '3'], imgHeight: 176, } componentDidMount() { // simulate img loading setTimeout(() => { this.setState({ data: [img, img2, img3], }); }, 100); } render() { return ( <Carousel autoplay={false} infinite beforeChange={(from, to) => console.log(`slide from ${from} to ${to}`)} afterChange={index => console.log('slide to', index)} > {this.state.data.map((val, index) => ( <a key={index} style={{ display: 'inline-block', width: '100%', height: this.state.imgHeight }} > <img src={val} alt="val" style={{ width: '100%', verticalAlign: 'top', height: this.state.imgHeight }} onLoad={() => { // fire window resize event to change height // 若用非手機模式打開,imgheight會由於整個瀏覽器的長度100%自適應形成100%全屏,手機模式加載則沒影響 window.dispatchEvent(new Event('resize')); this.setState({ imgHeight: 'auto' }) }} /> </a> ))} </Carousel> ) } } export default app
這裏也是封裝成組件作的前端
//src\components\Card\index.js import React, { Component } from 'react' import PropTypes from 'prop-types' import style from './index.less' class app extends Component { PropTypes={ list: PropTypes.array, className:PropTypes.string, } static defaultProps ={ list :[], } state = { } componentDidMount(){ } componentWillReceiveProps(nextProps){ } close=()=>{ this.setState({open:false}) this.props.onClose() } render() { const {list,className} = this.props return ( <div className={`${style["card-wrap"]} ${className}`}> { list.map((x,index)=>( <div className={style.card} key={index}> <div className="card-img"> <img src={x.src} alt="" /> </div> <div className="title"> {x.title} </div> </div> )) } </div> ) } } export default app
看總體的代碼是這個java
//src\pages\Home\index.js import React, { Component } from 'react' import { InputItem, Button, WhiteSpace, WingBlank, NoticeBar } from 'antd-mobile' //輪播組件 import Carousel from './model/Carousel' import Card from '@/components/Card' import img from '@/images/moon.png' class app extends Component { state = { title: '歡迎來到叭哩叭哩,github-https://github.com/1067011734/balibali!!!!' } cardList = [ { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, { title: '團員中秋', src: img }, ] render() { const { title } = this.state return ( <div className="page"> <NoticeBar marqueeProps={{ loop: true, style: { padding: '0 7.5px' } }}> {title} </NoticeBar> <Carousel /> <Card list={this.cardList}/> </div> ) } } export default app
在index.js中咱們引用appreact
//src\pages\index.js import React, { Component } from 'react' import { connect } from 'dva' import Transiton from '@/components/Transition' import '@/styles/base.less' class app extends Component { state = { } componentDidMount(){ } render() { return ( <Transiton {...this.props}> {this.props.children} </Transiton> ) } } export default app
//src\components\Transition\index.js import React, { Component } from 'react' import PropTypes from 'prop-types' import ReactCSSTransitionGroup from 'react-addons-css-transition-group' import StaticContainer from 'react-static-container' import style from './index.less' class app extends Component { PropTypes={ open: PropTypes.bool, onClose: PropTypes.func, } state = { previousPathname:null, } componentWillMount() { document.body.style.margin = "0px"; // 這是防止頁面被拖拽 document.body.addEventListener('touchmove', (ev) => { ev.preventDefault(); }); } componentWillReceiveProps(nextProps, nextContext) { if (nextProps.location.pathname !== this.props.location.pathname) { this.setState({ previousPathname: this.props.location.pathname }) } } componentDidUpdate() { if (this.state.previousPathname) { this.setState({ previousPathname: null }) } } render() { const {location,children,history} =this.props const {action} =history //goback:pop,push:push const className = action==='POP'?'translation-arrow-right':'translation-arrow-left' // const className = 'translation-arrow-left' const {pathname} =location const key = (pathname!=="/user")?'':"pathname" return ( <ReactCSSTransitionGroup component="div" className={`translation-wrap ${className}`} transitionName="translation" transitionEnterTimeout={300} transitionLeaveTimeout={300} // transitionLeave={false} > {/* 用key控制滑動權限----- 蒼天啊 */} <div key={key} className={`${pathname} translation-content`}> {children} </div> </ReactCSSTransitionGroup> ) } } export default app
其實我很好奇,下面的切換頁面在哪裏
下面的是login頁面
git
//src\pages\Login.js import React, { Component } from 'react' import { InputItem, Button, WhiteSpace, Toast } from 'antd-mobile' import { createForm } from 'rc-form' import { connect } from 'dva' import { loginReg } from '@regularConfig' import style from '@/styles/login.less' import avataSrc from '@/images/icon/avatar.png' import loginUserSrc from '@/images/icon/login_user.png' import loginPassSrc from '@/images/icon/login_pass.png' @createForm() @connect(({ login, loading }) => ({ login, submitting: loading.effects['login/login'], })) class app extends Component { state = { } submit = () => { const { getFieldProps, getFieldError } = this.props.form this.props.form.validateFields((error, values) => { if (error) { const msg = `請輸入${getFieldError('user') || ''}${getFieldError('password') || ''}` Toast.info(msg, 1); return } // this.props.history.push({ pathname: '/home' }) const { dispatch } = this.props dispatch({ type: 'common/login', payload: { ...values, }, }) }) } normalize = (val, prev) => { if (!loginReg.test(val)) { Toast.info('不能包含中文和大寫', 1); return prev } return val } render() { let errors const { getFieldProps } = this.props.form return ( <div className={`page ${style.login}`}> <div className={`${style["page-header"]}`}> <label>進入叭哩叭哩</label><img src={avataSrc} alt=""/> </div> <InputItem type="text" placeholder="帳號爲admin" maxLength="10" minLength="4" clear {...getFieldProps('user', { rules: [{ required: true, message: '帳號', }], normalize: this.normalize, })} > <img src={loginUserSrc} className='icon' /> </InputItem> <InputItem type="password" placeholder="密碼爲admin" maxLength="10" clear {...getFieldProps('password', { rules: [{ required: true, message: '密碼', }], normalize: this.normalize })} > <img src={loginPassSrc} className='icon' /> </InputItem> <WhiteSpace size="xl"/> <WhiteSpace size="xl"/> <Button type="primary" onClick={this.submit}>肯定</Button> <WhiteSpace /> </ div> ) } } export default app
路由也是會騙人的
程序員
//src\pages\404.js import React from 'react'; import Link from 'umi/link'; import Exception from '@/components/Exception'; export default () => ( <Exception type="404" style={{ minHeight: 500, height: '100%' }} linkElement={Link} /> );
//src\pages\User\index.js import React, { Component } from 'react' import { Steps,InputItem,Button,WhiteSpace} from 'antd-mobile' import Container from '@/components/Container' import { createForm } from 'rc-form' import { connect } from 'dva' import router from 'umi/router' import Step1 from './model/step1' import Step2 from './model/step2' import Step3 from './model/step3' const {Step} = Steps @createForm() @connect(({ common}) => ({ userInfo:common.userInfo })) class app extends Component { state = { userInfo:{ name:'', // 姓名 dept:'', // 所屬部門 person:[], //個性標籤 avatar:"" //頭像 }, stepNumber:1, } stepList=[ {title:'基本設置',key:1}, {title:'個性標籤',key:2}, {title:'上傳頭像',key:3}, ] componentWillMount(){ const {userInfo} = this.props this.setState({userInfo}) } componentDidMount(){ } // 步驟變化 stepSwitch=(key)=>{ const {userInfo} = this.state switch(key){ case 1: return <Step1 onChange={this.changeData} userInfo={userInfo} /> case 2: return <Step2 onChange={this.changeData} userInfo={userInfo} /> case 3: return <Step3 onChange={this.changeData} userInfo={userInfo} /> default: break; } } // 步驟完成保存數據 changeData=(data,key)=>{ const { dispatch } = this.props let {stepNumber,userInfo} = this.state stepNumber=key+1 if(stepNumber>3){ const params ={...userInfo,...data} dispatch({ type: 'common/updateUserInfo', payload: params, }) router.goBack() return } this.setState({ userInfo:{...userInfo,...data}, stepNumber, }) } render() { const { getFieldProps, getFieldError } = this.props.form const {stepNumber} = this.state return ( <Container title="我的設置"> <Steps direction="horizontal"> {this.stepList.map(item=><Step key={item.key} title={item.title} icon={<i className={`step-circle ${item.key<=stepNumber?'bj-primary':''}`}>{item.key<stepNumber?'√':item.key}</i>} status={item.key<stepNumber?'finish':'wait'} />)} </Steps> <WhiteSpace /> {this.stepSwitch(stepNumber)} {/* <Step1 /> */} </Container> ) } } export default app
//src\pages\User\model\step1.js import React, { Component } from 'react' import { List, InputItem, Switch, Stepper, Range, Button, Picker, Toast } from 'antd-mobile' import Container from '@/components/Container' import { createForm } from 'rc-form' import PropTypes from 'prop-types' const { Item } = List @createForm() class app extends Component { PropTypes = { onChange: PropTypes.func, } state = { } componentDidMount() { const { userInfo } = this.props const { name, dept } = userInfo // debugger this.props.form.setFieldsValue({ name, dept: [dept], }) } onSubmit = () => { const { getFieldError } = this.props.form this.props.form.validateFields((error, values) => { if (error) { const msg = `請輸入${getFieldError('name') || ''}${getFieldError('dept') || ''}` Toast.info(msg, 1); return } values.dept = values.dept[0] this.props.onChange(values, 1) }) } validateAccount = (rule, value, callback) => { if (value && value.length > 0) { callback(); } else { callback(new Error('At least four characters for account')); } } render() { const { getFieldProps, getFieldError } = this.props.form const deptData = [ { label: '前端攻城獅', value: '前端攻城獅', }, { label: 'java', value: 'java', }, ] return ( <form className="flex-column"> <List className="flex-3"> <InputItem {...getFieldProps('name', { rules: [{ required: true, message: '帳號', }], } )} clear placeholder="請修改帳號" >帳號 </InputItem> <Picker data={deptData} {...getFieldProps('dept', { rules: [{ required: true, message: '崗位', }], })} cols="1" > <List.Item arrow="horizontal">崗位</List.Item> </Picker> </List> <div className="flex-1"> <Button type="primary" onClick={this.onSubmit}>下一步</Button> </div> </form> ) } } export default app
//src\pages\User\model\step2.js import React, { Component } from 'react' import { Tag ,Button } from 'antd-mobile' import { createForm } from 'rc-form' import PropTypes from 'prop-types' import ReactDOM from 'react-dom' @createForm() class app extends Component { PropTypes={ onChange: PropTypes.func, } state = { } tagList=[ '樂觀','努力','積極','有愛心','勇敢','思想良好','積極向上','善於與相處','對工做積極','認真負責', '嚴格要求本身','有強烈的責任心' ] componentDidMount(){ } onSubmit = () => { const domSelect= ReactDOM.findDOMNode(this).querySelectorAll(".am-tag-active") const selectArr = Array.from(domSelect,x=>x.textContent) this.props.onChange({person:selectArr},2) } render() { const {userInfo} = this.props const {person} = userInfo return ( <div className="flex-column"> <div className="flex-3"> {this.tagList.map((x,index)=> <Tag key={index} selected={person&&person.includes(x)}>{x}</Tag> )} </div> <div className="flex-1"> <Button type="primary" onClick={this.onSubmit}>下一步</Button> </div> </div> ) } } export default app
//src\pages\User\model\step3.js
import React, { Component } from 'react'
import { ImagePicker ,Button } from 'antd-mobile'
import { createForm } from 'rc-form'
import PropTypes from 'prop-types'github
@createForm()
class app extends Component {
PropTypes={
onChange: PropTypes.func,
}web
state = { files: [], }
componentDidMount(){
const {userInfo} = this.props
const {avatar} = userInfo
this.setState({files:[{url:avatar}]})
}redux
onSubmit = () => {
const {files} = this.state
this.props.onChange({avatar:files[0]?files[0].url:''},3)
}瀏覽器
onChange = (files, type, index) => {
console.log(files, type, index);
this.setState({
files,
})
}
render() { const { files } = this.state return ( <div className="flex-column"> <div className="flex-3"> <ImagePicker files={files} onChange={this.onChange} onImageClick={(index, fs) => console.log(index, fs)} selectable={files.length < 1} accept="image/gif,image/jpeg,image/jpg,image/png" /> </div> <div className="flex-1"> <Button type="primary" onClick={this.onSubmit}>保存</Button> </div> </div> ) }
}
export default app
![](https://img2018.cnblogs.com/blog/1037363/201905/1037363-20190522180524238-1345634353.png)
js
//src\pages\Map\index.js
import React, { Component } from 'react'
import { createForm } from 'rc-form'
import Bmap from '@/components/Bmap'
import { connect } from 'dva'
@createForm()
@connect(({ login, loading }) => ({
login,
submitting: loading.effects['login/login'],
}))
class app extends Component {
state = {
}
componentDidMount(){
console.info(this.props)
}
render() { return ( <div className="page"> <Bmap /> </div> ) }
}
export default app
js
//src\components\Bmap\index.js
//定義地圖組件
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import BMap from 'BMap'
import style from './index.less'
class app extends Component {
PropTypes={
info: PropTypes.string,
}
state = {
info:'叭哩叭哩'
}
map={}
componentDidMount () {
const map = new BMap.Map("Bmap"); // 建立Map實例
this.map=map
this.mapInit(map)
}
mapInit=(map)=>{
const _self = this
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);
map.addControl(new BMap.MapTypeControl()); // 添加地圖類型控件
map.enableScrollWheelZoom(true); // 開啓鼠標滾輪縮放
// const top_left_control = new BMap.ScaleControl({anchor: BMAP_ANCHOR_TOP_LEFT});// 左上角,添加比例尺
const top_left_navigation = new BMap.NavigationControl(); // 左上角,添加默認縮放平移控件
// const top_right_navigation = new BMap.NavigationControl({anchor: BMAP_ANCHOR_TOP_RIGHT, type: BMAP_NAVIGATION_CONTROL_SMALL}); // 右上角,僅包含平移和縮放按鈕
// map.addControl(top_left_control);
map.addControl(top_left_navigation);
// map.addControl(top_right_navigation);
// 定位 const point = new BMap.Point(116.331398,39.897445); map.centerAndZoom(point,15);// 初始化地圖,設置中心點座標和地圖級別 const geolocation = new BMap.Geolocation(); geolocation.getCurrentPosition(function(r){ if(this.getStatus() == BMAP_STATUS_SUCCESS){ const mk = new BMap.Marker(r.point); map.addOverlay(mk); map.panTo(r.point); _self.setWindowInfo(mk) // alert('您的位置:'+r.point.lng+','+r.point.lat); } else { // alert('failed'+this.getStatus()); } },{enableHighAccuracy: true})
}
setWindowInfo=(marker)=>{
const {info} = this.state
marker.addEventListener("click",(e) => {
this.openInfo(info,e)}
);
}
// 建立並打開信息窗口
openInfo=(content,e)=>{
const opts = {
width : 250, // 信息窗口寬度
height: 80, // 信息窗口高度
title : "信息窗口" , // 信息窗口標題
enableMessage:true// 設置容許信息窗發送短息
}
const map =this.map
const p = e.target;
const point = new BMap.Point(p.getPosition().lng, p.getPosition().lat);
const infoWindow = new BMap.InfoWindow(content,opts); // 建立信息窗口對象
map.openInfoWindow(infoWindow,point); // 開啓信息窗口
}
render() {
return (
export default app
![](https://img2018.cnblogs.com/blog/1037363/201905/1037363-20190522180737167-1136189469.png)
js
//這個比較厲害,還有下拉加載刷新
//src\pages\Book\index.js import React, { Component } from 'react' import ReactDOM from 'react-dom' // import { InputItem,Button, WhiteSpace, WingBlank } from 'antd-mobile' // import data from './data' import { PullToRefresh, ListView, Button } from 'antd-mobile'; const data = [ { img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png', title: 'Meet hotel', des: '不是全部的兼職汪都須要風吹日曬', }, { img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png', title: 'McDonald\'s invites you', des: '不是全部的兼職汪都須要風吹日曬', }, { img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png', title: 'Eat the week', des: '不是全部的兼職汪都須要風吹日曬', }, ]; const NUM_ROWS = 20; let pageIndex = 0; function genData(pIndex = 0) { const dataArr = []; for (let i = 0; i < NUM_ROWS; i++) { dataArr.push(`row - ${(pIndex * NUM_ROWS) + i}`); } return dataArr; } class app extends React.Component { constructor(props) { super(props); const dataSource = new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2, }); this.state = { dataSource, refreshing: true, isLoading: true, height: document.documentElement.clientHeight, useBodyScroll: false, }; } // If you use redux, the data maybe at props, you need use `componentWillReceiveProps` // componentWillReceiveProps(nextProps) { // if (nextProps.dataSource !== this.props.dataSource) { // this.setState({ // dataSource: this.state.dataSource.cloneWithRows(nextProps.dataSource), // }); // } // } componentDidUpdate() { if (this.state.useBodyScroll) { document.body.style.overflow = 'auto'; } else { document.body.style.overflow = 'hidden'; } } componentDidMount() { const hei = this.state.height - ReactDOM.findDOMNode(this.lv).offsetTop; setTimeout(() => { this.rData = genData(); this.setState({ dataSource: this.state.dataSource.cloneWithRows(genData()), height: hei, refreshing: false, isLoading: false, }); }, 1500); } onRefresh = () => { this.setState({ refreshing: true, isLoading: true }); // simulate initial Ajax setTimeout(() => { this.rData = genData(); this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.rData), refreshing: false, isLoading: false, }); }, 600); }; onEndReached = (event) => { // load new data // hasMore: from backend data, indicates whether it is the last page, here is false if (this.state.isLoading && !this.state.hasMore) { return; } console.log('reach end', event); this.setState({ isLoading: true }); setTimeout(() => { this.rData = [...this.rData, ...genData(++pageIndex)]; this.setState({ dataSource: this.state.dataSource.cloneWithRows(this.rData), isLoading: false, }); }, 1000); }; render() { const separator = (sectionID, rowID) => ( <div key={`${sectionID}-${rowID}`} style={{ backgroundColor: '#F5F5F9', height: 8, borderTop: '1px solid #ECECED', borderBottom: '1px solid #ECECED', }} /> ); let index = data.length - 1; const row = (rowData, sectionID, rowID) => { if (index < 0) { index = data.length - 1; } const obj = data[index--]; return ( <div key={rowID} style={{ padding: '0 15px', backgroundColor: 'white', }} > <div style={{ height: '50px', lineHeight: '50px', color: '#888', fontSize: '18px', borderBottom: '1px solid #ddd' }}> {obj.title} </div> <div style={{ display: '-webkit-box', display: 'flex', padding: '15px' }}> <img style={{ height: '63px', width: '63px', marginRight: '15px' }} src={obj.img} alt="" /> <div style={{ display: 'inline-block' }}> <div style={{ marginBottom: '8px', color: '#000', fontSize: '16px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '250px' }}>{obj.des}-{rowData}</div> <div style={{ fontSize: '16px' }}><span style={{ fontSize: '30px', color: '#FF6E27' }}>{rowID}</span> 元/任務</div> </div> </div> </div> ); }; return (<div> <ListView ref={el => this.lv = el} dataSource={this.state.dataSource} renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}> {this.state.isLoading ? 'Loading...' : 'Loaded'} </div>)} renderRow={row} renderSeparator={separator} useBodyScroll={false} style={{ height: this.state.height, border: '1px solid #ddd', margin: '5px 0', }} pullToRefresh={<PullToRefresh refreshing={this.state.refreshing} onRefresh={this.onRefresh} />} onEndReached={this.onEndReached} pageSize={5} /> </div>); } } export default app