第一篇 https://segmentfault.com/a/11...

在上一篇文章中教给大家了怎么搭建项目的架子;那么今天我们就来说一下项目里的导航和列表的实现

导航

废话不说啦 下面直接给大家讲一下代码
项目用的antd-mobile的框架 应该没什么难度,我相信大家认真看文档的都能布局出来;

TabButton.js

  1. import React, { Component } from 'react';
  2. import { Tabs, WhiteSpace,ListView,Toast} from 'antd-mobile';
  3. import { routerRedux } from 'dva/router';
  4. import { connect } from 'dva';
  5. import Request from '../common/fetch'
  6. import {width,height} from '../common/style';
  7. const TabPane = Tabs.TabPane;
  8. class TabButton extends Component {
  9. constructor(props) {
  10. super(props);
  11. this.state = {
  12. channels: []
  13. }
  14. }
  15. componentDidMount() {
  16. // 这个地方是封装的fetch请求;
  17. Request('/api/article/channel',{
  18. secret:1111,
  19. },((res) => {
  20. this.setState({
  21. channels: res.result.channels
  22. })
  23. // 请求过来的数据全部存下来,以便后期调用,同时可以减少请求
  24. this.props.dispatch({
  25. type: 'indexList/TabData',
  26. payload: res.result.channels,
  27. });
  28. }))
  29. }
  30. //这个点需要注意:此处是将click事件传递给子组件,另一界面 <TabButton ButtonClick ={this.ButtonClick.bind(this)} />就可以取到此组件传递过去的click事件;
  31. _handleTabClick(key){
  32. this.props.ButtonClick(key);
  33. }
  34. _renderList() {
  35. let result = [];
  36. const channels = this.state.channels;
  37. for(let i in channels) {
  38. if(channels[i].attval == 1 || channels[i].attval == 2){
  39. result.push(
  40. <TabPane tab={`${channels[i].title}`} key={`${parseInt(channels[i].ID)}`}>
  41. <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center',border:'none' }}>
  42. </div>
  43. </TabPane>
  44. )
  45. }
  46. }
  47. return result
  48. }
  49. _getMore() {
  50. this.props.dispatch(
  51. routerRedux.push('/moreChannel')
  52. )
  53. }
  54. render() {
  55. return(
  56. <div style={{position:'fixed',top:44,zIndex:999,backgroundColor:'#fff',width:(width/7)*6}}>
  57. <Tabs defaultActiveKey="1"
  58. pageSize={7}
  59. onTabClick={(key) => {this._handleTabClick(key)}}
  60. swipeable = {false}
  61. >
  62. {this._renderList()}
  63. </Tabs>
  64. <p style={styles.moreChannel} onClick={() => this._getMore()}>
  65. <img style={{width:26,height:26,marginTop:8,marginLeft:14}} src={require('../../assets/list/addchannel@2x.png')} alt=""/>
  66. </p>
  67. </div>
  68. )
  69. }
  70. }
  71. const styles = {
  72. moreChannel:{
  73. position:'absolute',
  74. top:0,
  75. right:-width/7,
  76. zIndex:9999,
  77. width:width/7,
  78. height:42,
  79. backgroundColor:'#fff',
  80. alignItems:'center',
  81. justifyContent:'center'
  82. }
  83. }
  84. function indexList({indexList}) {
  85. return { indexList };
  86. }
  87. export default connect(indexList)(TabButton);

fetch.js

  1. export default function Request(url,body,callback){
  2. fetch(url,{
  3. method: 'POST',
  4. mode: "cors",
  5. headers: {
  6. 'Content-Type': 'application/json',
  7. 'Accept': 'application/json'
  8. },
  9. body: JSON.stringify(body)
  10. }).then((res) => res.json()).then((res) => {
  11. callback(res)
  12. }).catch((err) => {
  13. console.log(err)
  14. })
  15. }

列表

indexTab.js

  1. import React, { Component,PureComponent,PropTypes } from 'react';
  2. import { Tabs, WhiteSpace,ListView,Toast} from 'antd-mobile';
  3. import { routerRedux } from 'dva/router';
  4. import { connect } from 'dva';
  5. import ReactPullLoad,{ STATS } from 'react-pullload';
  6. import TabButton from './TabButton';
  7. import {width,height} from '../common/style';
  8. let devicenum = localStorage.getItem('devicenum')
  9. const loadMoreLimitNum = 10;
  10. const defaultStyle ={
  11. width: "100%",
  12. textAlign: "center",
  13. fontSize: "14px",
  14. lineHeight: "1.5",
  15. paddingTop:"12px",
  16. color:'#ccc'
  17. }
  18. class HeadNode extends PureComponent{
  19. static propTypes = {
  20. loaderState: PropTypes.string.isRequired,
  21. };
  22. static defaultProps = {
  23. loaderState: STATS.init,
  24. };
  25. render(){
  26. const {
  27. loaderState
  28. } = this.props
  29. let content = ""
  30. if(loaderState == STATS.pulling){
  31. content = "下拉刷新"
  32. } else if(loaderState == STATS.enough){
  33. content = "松开刷新"
  34. } else if(loaderState == STATS.refreshing){
  35. content = "正在刷新..."
  36. } else if(loaderState == STATS.refreshed){
  37. content = "刷新成功"
  38. }
  39. return(
  40. <div style={defaultStyle}>
  41. {content}
  42. </div>
  43. )
  44. }
  45. }
  46. class FooterNode extends PureComponent{
  47. static propTypes = {
  48. loaderState: PropTypes.string.isRequired,
  49. hasMore: PropTypes.bool.isRequired
  50. };
  51. static defaultProps = {
  52. loaderState: STATS.init,
  53. hasMore: true
  54. };
  55. render(){
  56. const {
  57. loaderState,
  58. hasMore
  59. } = this.props
  60. let content = ""
  61. if(loaderState == STATS.loading){
  62. return(
  63. <div style={defaultStyle}>
  64. <img src={require('../../assets/state/fail@2x.png')} alt="" style={{width:32,height:40}} />
  65. <span>正在加載喔~</span>
  66. </div>
  67. )
  68. } else if(hasMore === false){
  69. content = "没有更多"
  70. }
  71. return(
  72. <div style={defaultStyle}>
  73. {content}
  74. </div>
  75. )
  76. }
  77. }
  78. class indexTab extends Component {
  79. constructor(props) {
  80. super(props)
  81. this.state = {
  82. channels : [],
  83. channelid : 1,
  84. showT:false,
  85. loading : false,
  86. hasMore: true,
  87. data: [],
  88. action: STATS.init,
  89. index: loadMoreLimitNum,
  90. newsLength:''
  91. }
  92. }
  93. componentDidMount() {
  94. this.getListData(this.state.channelid);
  95. }
  96. getListData(channelid) {
  97. // List
  98. fetch('/api/article',{
  99. method: 'POST',
  100. mode: "cors",
  101. headers: {
  102. 'Content-Type': 'application/json',
  103. 'Accept': 'application/json'
  104. },
  105. body: JSON.stringify({
  106. channelID: channelid,
  107. type: 0,
  108. pageSize: 10,
  109. dt : 2,
  110. action: 1,
  111. devicenum:devicenum
  112. })
  113. }).then((res) => res.json()).then((res) => {
  114. this.setState({
  115. data: res.result.news,
  116. newsLength:res.result.news.length
  117. })
  118. this.props.dispatch({
  119. type: 'indexList/detailData',
  120. payload: res.result.news,
  121. });
  122. }).then(() => {
  123. setTimeout(() => {
  124. this.setState({
  125. showT : true
  126. })
  127. },1900)
  128. }).then(() => {
  129. setTimeout(() => {
  130. this.setState({
  131. showT : false
  132. })
  133. },2900)
  134. }).catch((err) => {
  135. console.log(err)
  136. })
  137. }
  138. handleAction = (action) => {
  139. console.info(action, this.state.action,action === this.state.action);
  140. if(action === this.state.action){
  141. return false
  142. }
  143. if(action === STATS.refreshing){//刷新
  144. this.handRefreshing();
  145. } else if(action === STATS.loading){
  146. this.handLoadMore();
  147. } else{
  148. this.setState({
  149. action: action
  150. })
  151. }
  152. }
  153. handRefreshing = () =>{
  154. if(STATS.refreshing === this.state.action){
  155. return false
  156. }
  157. this.getListData(this.state.channelid)
  158. setTimeout(()=>{
  159. this.setState({
  160. action: STATS.refreshed,
  161. index: loadMoreLimitNum
  162. });
  163. }, 3000)
  164. }
  165. handLoadMore = () => {
  166. if(STATS.loading === this.state.action){
  167. return false
  168. }
  169. setTimeout(()=>{
  170. if(this.state.index === 0){
  171. this.setState({
  172. action: STATS.reset,
  173. hasMore: false
  174. });
  175. } else{
  176. fetch('/api/article',{
  177. method: 'POST',
  178. headers: {
  179. 'Content-Type': 'application/json;charset=UTF-8',
  180. 'Accept': 'application/json'
  181. },
  182. body: JSON.stringify({
  183. channelID: this.state.channelid,
  184. type: 0,
  185. pageSize: 10,
  186. dt : 2,
  187. action: 1,
  188. devicenum:devicenum
  189. })
  190. }).then((res) => res.json()).then((res) => {
  191. this.setState({
  192. data: [...this.state.data,...res.result.news],
  193. action: STATS.reset,
  194. index: this.state.index - 1
  195. })
  196. this.props.dispatch({
  197. type: 'indexList/detailData',
  198. payload: [...this.state.data,...res.result.news],
  199. });
  200. }).then(() => {
  201. console.log(this.state.showT)
  202. setTimeout(() => {
  203. this.setState({
  204. showT : true
  205. })
  206. },1900)
  207. }).then(() => {
  208. setTimeout(() => {
  209. this.setState({
  210. showT : false
  211. })
  212. },2900)
  213. }).catch((err) => {
  214. console.log(err)
  215. })
  216. }
  217. }, 3000)
  218. this.setState({
  219. action: STATS.loading
  220. })
  221. }
  222. //跳转到详情页
  223. _routerDetail(index) {
  224. localStorage.setItem('detailid',index)
  225. this.props.dispatch(
  226. routerRedux.push(`/detail/${index}`)
  227. )
  228. }
  229. //Tab 切换重新调取
  230. ButtonClick(key) {
  231. this.getListData(key);
  232. this.setState({
  233. channelid:key
  234. })
  235. }
  236. _renderShow() {
  237. if(this.state.showT == true){
  238. if(this.state.newsLength != 0){
  239. return(
  240. <p style={styles.more}>更新了{this.state.newsLength}条内容</p>
  241. )
  242. }else{
  243. return(
  244. <p style={styles.more}>暂無更新推送</p>
  245. )
  246. }
  247. }else{
  248. return(
  249. <p></p>
  250. )
  251. }
  252. }
  253. render(){
  254. const {data,hasMore} = this.state
  255. return (
  256. <div>
  257. <TabButton
  258. ButtonClick = {this.ButtonClick.bind(this)}
  259. />
  260. <p style={{width:100,height:80}}></p>
  261. <ReactPullLoad
  262. downEnough={50}
  263. action={this.state.action}
  264. handleAction={this.handleAction}
  265. hasMore={hasMore}
  266. distanceBottom={10}
  267. HeadNode={HeadNode}
  268. FooterNode={FooterNode}
  269. >
  270. <ul className="test-ul">
  271. {
  272. data.map( (str, index )=>{
  273. if(str.images[0] != ''){
  274. return <li key={index}>
  275. <div style={styles.news} onClick = {() => this._routerDetail(index)}>
  276. <img src={str.images[0]} style={styles.imgStyle} />
  277. <p style={styles.newsTitle}>{str.title}</p>
  278. <p style={{fontSize:12,color:'#ccc',borderWidth:1}}><span style={{color:'#03D7FF'}}>{str.source}</span> | {str.publishTime}</p>
  279. </div>
  280. </li>
  281. }else{
  282. return <li key={index}>
  283. <div style={styles.news} onClick = {() => this._routerDetail(index)}>
  284. <p style={styles.newsTitle}>{str.title}</p>
  285. <p style={{fontSize:12,color:'#ccc',borderWidth:1}}><span style={{color:'#03D7FF'}}>{str.source}</span> | {str.publishTime}</p>
  286. </div>
  287. </li>
  288. }
  289. })
  290. }
  291. </ul>
  292. </ReactPullLoad>
  293. <div>
  294. </div>
  295. {this._renderShow()}
  296. </div>
  297. )
  298. }
  299. }
  300. const styles = {
  301. more: {
  302. width:width,
  303. backgroundColor:'#FFDB01',
  304. position:'absolute',
  305. zIndex:9999,
  306. top:86,
  307. textAlign:'center',
  308. padding:5,
  309. fontSize:14,
  310. display:'block',
  311. },
  312. news: {
  313. padding:15,
  314. justifyContent:'center',
  315. alignItems:'center'
  316. },
  317. imgStyle: {
  318. width:width-30,
  319. //height:100
  320. },
  321. newsTitle: {
  322. fontSize:18,
  323. marginTop:10,
  324. marginBottom:10
  325. },
  326. moreTab: {
  327. width:width-(width/7)*6,
  328. height:43,
  329. backgroundColor:'#fff',
  330. position: 'absolute',
  331. justifyContent:'center',
  332. alignItems:'center',
  333. top:44,
  334. right:0,
  335. zIndex:9999
  336. }
  337. }
  338. function indexList({ indexList }) {
  339. return { indexList };
  340. }
  341. export default connect(indexList)(indexTab);

好啦 上述就是整个首页的主要代码,知道如何创建项目的你们可以尝试啦~~~

React+dva+webpack+antd-mobile 实战分享(二)的更多相关文章

  1. Vue2+VueRouter2+webpack 构建项目实战(二):目录以及文件结构

    通过上一篇博文<Vue2+VueRouter2+webpack 构建项目实战(一):准备工作>,我们已经新建好了一个基于vue+webpack的项目.本篇文章详细介绍下项目的结构. 项目目 ...

  2. React+dva+webpack+antd-mobile 实战分享(一)

    再看本篇文章之前,本人还是建议想入坑react的童鞋可以选有create-react-app来创建react的项目,因为现在dva和roadhog还不成熟,坑相对要多一些,当然如果你已经做好跳坑的准备 ...

  3. webpack+vue项目实战(四,前端与后端的数据交互和前端展示数据)

    地址:https://segmentfault.com/a/1190000010063757 1.前言 今天要做的,就是在上一篇文章的基础上,进行功能页面的开发.简单点说呢,就是与后端的数据交互和怎么 ...

  4. [书籍精读]《React Native精解与实战》精读笔记分享

    写在前面 书籍介绍:本书由架构师撰写,包含ReactNative框架底层原理,以及与iOS.Android混合开发案例,精选了大量实例代码,方便读者快速学习.主要内容分为两大部分,第1部分" ...

  5. webpack入门和实战(二):全面理解和运用loader和plugins

    您的阅读目录: 一.理解webpack加载器loader 二.理解less-loader加载器的使用 三.理解babel-loader加载器的使用 四.webpack命令行常见使用的操作 五.用web ...

  6. 《React Native 精解与实战》书籍连载「React 与 React Native 简介」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  7. 十九、React UI框架Antd(Ant Design)的使用——及react Antd的使用 button组件 Icon组件 Layout组件 DatePicker日期组件

    一.Antd(Ant Design)的使用:引入全部Css样式 1.1 antd官网: https://ant.design/docs/react/introduce-cn 1.2 React中使用A ...

  8. 非阻塞同步算法实战(二)-BoundlessCyclicBarrier

    本人是本文的作者,首发于ifeve(非阻塞同步算法实战(二)-BoundlessCyclicBarrier) 前言 相比上一 篇而言,本文不需要太多的准备知识,但技巧性更强一些.因为分析.设计的过程比 ...

  9. webpack入门和实战(一):webpack配置及技巧

    一.全面理解webpack 1.什么是 webpack? webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).coffee.样式(含less/sass).图片等都 ...

随机推荐

  1. burpsuite中文乱码问题

    无法显示中文 先查看网页编码 然后在修改编码为网页的编码一致并重启burpsuite即可(或者直接选第一项自动识别编码) 显示中文正常,但是无法匹配中文 找到了一处验证码漏洞,想用burpsuite的 ...

  2. (六)目标检测算法之YOLO

    系列文章链接: (一)目标检测概述 https://www.cnblogs.com/kongweisi/p/10894415.html (二)目标检测算法之R-CNN https://www.cnbl ...

  3. SQL注入 - SQLi-Labs靶场过关记录

    Less-1 1.看报错类型,确定注入点 ?id=1' order by 4--++ 2.确定数据库 ?id=-1' union select 1,2,3--++ 3.查看数据库 ?id=-1' un ...

  4. 华为交换机ACL如何使用及原则

    华为交换机ACL如何使用及原则 转载自:辣条①号  源链接:https://boke.wsfnk.com/archives/480.html   ACL(访问控制列表)的应用原则:标准ACL,尽量用在 ...

  5. C++高并发场景下读多写少的优化方案

    概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...

  6. 阿里云服务器搭建vulhub靶场

    阿里云服务器搭建vulhub靶场 环境 服务器:阿里云服务器 系统:centos7 应用:vulhub 步骤 vulhub需要依赖docker搭建,首先安装docker. 使用以下方法之一: # cu ...

  7. 版本控制工具之git的常用命名01

    基本命令 从git远程仓库导出项目:git clone http://127.0.0.1/name_master/server.git 从git远程仓库拉取代码:git pull 查看本地仓库状态:g ...

  8. 羽夏看Win系统内核—— x64 番外篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  9. v-if 与 v-for 同时使用会报错

    在进行项目开发的时候因为在一个标签上同时使用了v-for和v-if两个指令导致的报错. 报错代码如下: <el-input type="textarea" :autosize ...

  10. Spring Boot 传参 序列化和反序列化

    序列化 反序列化