第一篇 https://segmentfault.com/a/11...
在上一篇文章中教给大家了怎么搭建项目的架子;那么今天我们就来说一下项目里的导航和列表的实现
导航
废话不说啦 下面直接给大家讲一下代码
项目用的antd-mobile的框架 应该没什么难度,我相信大家认真看文档的都能布局出来;
TabButton.js
import React,{ Component } from 'react'; import { Tabs,WhiteSpace,ListView,Toast} from 'antd-mobile'; import { routerRedux } from 'dva/router'; import { connect } from 'dva'; import Request from '../common/fetch' import {width,height} from '../common/style'; const TabPane = Tabs.TabPane; class TabButton extends Component { constructor(props) { super(props); this.state = { channels: [] } } componentDidMount() { // 这个地方是封装的fetch请求; Request('/api/article/channel',{ secret:1111,},((res) => { this.setState({ channels: res.result.channels }) // 请求过来的数据全部存下来,以便后期调用,同时可以减少请求 this.props.dispatch({ type: 'indexList/TabData',payload: res.result.channels,}); })) } //这个点需要注意:此处是将click事件传递给子组件,另一界面 <TabButton ButtonClick ={this.ButtonClick.bind(this)} />就可以取到此组件传递过去的click事件; _handleTabClick(key){ this.props.ButtonClick(key); } _renderList() { let result = []; const channels = this.state.channels; for(let i in channels) { if(channels[i].attval == 1 || channels[i].attval == 2){ result.push( <TabPane tab={`${channels[i].title}`} key={`${parseInt(channels[i].ID)}`}> <div style={{ display: 'flex',alignItems: 'center',justifyContent: 'center',border:'none' }}> </div> </TabPane> ) } } return result } _getMore() { this.props.dispatch( routerRedux.push('/moreChannel') ) } render() { return( <div style={{position:'fixed',top:44,zIndex:999,backgroundColor:'#fff',width:(width/7)*6}}> <Tabs defaultActiveKey="1" pageSize={7} onTabClick={(key) => {this._handleTabClick(key)}} swipeable = {false} > {this._renderList()} </Tabs> <p style={styles.moreChannel} onClick={() => this._getMore()}> <img style={{width:26,height:26,marginTop:8,marginLeft:14}} src={require('../../assets/list/addchannel@2x.png')} alt=""/> </p> </div> ) } } const styles = { moreChannel:{ position:'absolute',top:0,right:-width/7,zIndex:9999,width:width/7,height:42,alignItems:'center',justifyContent:'center' } } function indexList({indexList}) { return { indexList }; } export default connect(indexList)(TabButton);
fetch.js
export default function Request(url,body,callback){ fetch(url,{ method: 'POST',mode: "cors",headers: { 'Content-Type': 'application/json','Accept': 'application/json' },body: JSON.stringify(body) }).then((res) => res.json()).then((res) => { callback(res) }).catch((err) => { console.log(err) }) }
列表
indexTab.js
import React,{ Component,PureComponent,PropTypes } from 'react'; import { Tabs,Toast} from 'antd-mobile'; import { routerRedux } from 'dva/router'; import { connect } from 'dva'; import ReactPullLoad,{ STATS } from 'react-pullload'; import TabButton from './TabButton'; import {width,height} from '../common/style'; let devicenum = localStorage.getItem('devicenum') const loadMoreLimitNum = 10; const defaultStyle ={ width: "100%",textAlign: "center",fontSize: "14px",lineHeight: "1.5",paddingTop:"12px",color:'#ccc' } class HeadNode extends PureComponent{ static propTypes = { loaderState: PropTypes.string.isrequired,}; static defaultProps = { loaderState: STATS.init,}; render(){ const { loaderState } = this.props let content = "" if(loaderState == STATS.pulling){ content = "下拉刷新" } else if(loaderState == STATS.enough){ content = "松开刷新" } else if(loaderState == STATS.refreshing){ content = "正在刷新..." } else if(loaderState == STATS.refreshed){ content = "刷新成功" } return( <div style={defaultStyle}> {content} </div> ) } } class FooterNode extends PureComponent{ static propTypes = { loaderState: PropTypes.string.isrequired,hasMore: PropTypes.bool.isrequired }; static defaultProps = { loaderState: STATS.init,hasMore: true }; render(){ const { loaderState,hasMore } = this.props let content = "" if(loaderState == STATS.loading){ return( <div style={defaultStyle}> <img src={require('../../assets/state/fail@2x.png')} alt="" style={{width:32,height:40}} /> <span>正在加載喔~</span> </div> ) } else if(hasMore === false){ content = "没有更多" } return( <div style={defaultStyle}> {content} </div> ) } } class indexTab extends Component { constructor(props) { super(props) this.state = { channels : [],channelid : 1,showT:false,loading : false,hasMore: true,data: [],action: STATS.init,index: loadMoreLimitNum,newsLength:'' } } componentDidMount() { this.getListData(this.state.channelid); } getListData(channelid) { // List fetch('/api/article',{ method: 'POST',headers: { 'Content-Type': 'application/json','Accept': 'application/json' },body: JSON.stringify({ channelID: channelid,type: 0,pageSize: 10,dt : 2,action: 1,devicenum:devicenum }) }).then((res) => res.json()).then((res) => { this.setState({ data: res.result.news,newsLength:res.result.news.length }) this.props.dispatch({ type: 'indexList/detailData',payload: res.result.news,}); }).then(() => { setTimeout(() => { this.setState({ showT : true }) },1900) }).then(() => { setTimeout(() => { this.setState({ showT : false }) },2900) }).catch((err) => { console.log(err) }) } handleAction = (action) => { console.info(action,this.state.action,action === this.state.action); if(action === this.state.action){ return false } if(action === STATS.refreshing){//刷新 this.handRefreshing(); } else if(action === STATS.loading){ this.handLoadMore(); } else{ this.setState({ action: action }) } } handRefreshing = () =>{ if(STATS.refreshing === this.state.action){ return false } this.getListData(this.state.channelid) setTimeout(()=>{ this.setState({ action: STATS.refreshed,index: loadMoreLimitNum }); },3000) } handLoadMore = () => { if(STATS.loading === this.state.action){ return false } setTimeout(()=>{ if(this.state.index === 0){ this.setState({ action: STATS.reset,hasMore: false }); } else{ fetch('/api/article',{ method: 'POST',headers: { 'Content-Type': 'application/json;charset=UTF-8','Accept': 'application/json' },body: JSON.stringify({ channelID: this.state.channelid,devicenum:devicenum }) }).then((res) => res.json()).then((res) => { this.setState({ data: [...this.state.data,...res.result.news],action: STATS.reset,index: this.state.index - 1 }) this.props.dispatch({ type: 'indexList/detailData',payload: [...this.state.data,}); }).then(() => { console.log(this.state.showT) setTimeout(() => { this.setState({ showT : true }) },1900) }).then(() => { setTimeout(() => { this.setState({ showT : false }) },2900) }).catch((err) => { console.log(err) }) } },3000) this.setState({ action: STATS.loading }) } //跳转到详情页 _routerDetail(index) { localStorage.setItem('detailid',index) this.props.dispatch( routerRedux.push(`/detail/${index}`) ) } //Tab 切换重新调取 ButtonClick(key) { this.getListData(key); this.setState({ channelid:key }) } _renderShow() { if(this.state.showT == true){ if(this.state.newsLength != 0){ return( <p style={styles.more}>更新了{this.state.newsLength}条内容</p> ) }else{ return( <p style={styles.more}>暂無更新推送</p> ) } }else{ return( <p></p> ) } } render(){ const {data,hasMore} = this.state return ( <div> <TabButton ButtonClick = {this.ButtonClick.bind(this)} /> <p style={{width:100,height:80}}></p> <ReactPullLoad downEnough={50} action={this.state.action} handleAction={this.handleAction} hasMore={hasMore} distanceBottom={10} HeadNode={HeadNode} FooterNode={FooterNode} > <ul className="test-ul"> { data.map( (str,index )=>{ if(str.images[0] != ''){ return <li key={index}> <div style={styles.news} onClick = {() => this._routerDetail(index)}> <img src={str.images[0]} style={styles.imgStyle} /> <p style={styles.newsTitle}>{str.title}</p> <p style={{fontSize:12,color:'#ccc',borderWidth:1}}><span style={{color:'#03D7FF'}}>{str.source}</span> | {str.publishTime}</p> </div> </li> }else{ return <li key={index}> <div style={styles.news} onClick = {() => this._routerDetail(index)}> <p style={styles.newsTitle}>{str.title}</p> <p style={{fontSize:12,borderWidth:1}}><span style={{color:'#03D7FF'}}>{str.source}</span> | {str.publishTime}</p> </div> </li> } }) } </ul> </ReactPullLoad> <div> </div> {this._renderShow()} </div> ) } } const styles = { more: { width:width,backgroundColor:'#FFDB01',position:'absolute',top:86,textAlign:'center',padding:5,fontSize:14,display:'block',news: { padding:15,justifyContent:'center',alignItems:'center' },imgStyle: { width:width-30,//height:100 },newsTitle: { fontSize:18,marginTop:10,marginBottom:10 },moreTab: { width:width-(width/7)*6,height:43,position: 'absolute',right:0,zIndex:9999 } } function indexList({ indexList }) { return { indexList }; } export default connect(indexList)(indexTab);