菜单

「iOS」行车服务app 「客户端、后端思路+代码」11.React实战开发–适配PC及动端的情报头长条网站。

2018年9月24日 - 中甲报道
  • 多年来开了一个行车服务项目,iOS客户端应用Objective-C编纂,
    后端采用PHP搭建,部署在阿里云,操作系统也Linux CentOS 7.3,数据库MySQL,服务器也Apache,是于基础之LAMP组合。

github:https://github.com/Ching-Lee/react\_news

1.条件搭建

第一上总体的职能图:

1.1 使用react脚本架搭建项目(详情参见第一节)

创办项目:
入项目目录执行如下命令。

create-react-app react-news

betway体育 1

betway体育 2

进项目目录

cd react-news
npm start

betway体育 3

betway体育 4

3000端口

清除App.js中之始末

import React, { Component } from 'react';

import './App.css';

class App extends Component {
  render() {
    return (
     <h1>Init</h1>
    );
  }
}

export default App;

betway体育 5

betway体育 6

1.2 引入Ant Design的UI框架

https://ant.design/docs/react/use-with-create-react-app-cn

npm install --save antd

betway体育 7

betway体育 8

import React, { Component } from 'react';

import './App.css';
import Button from 'antd/lib/button';

class App extends Component {
  render() {
    return (
        <div>
          <h1>Init</h1>
          <Button type="primary">Button</Button>
        </div>
    );
  }
}

export default App;

@import '~antd/dist/antd.css';

.App {
  text-align: center;
}
....
![](https://upload-images.jianshu.io/upload_images/8521343-4302edb12ac59ff9.png)

效果如图

show.gif

2.路是因为安排

npm install react-router@2.8 --save

于app.js中以媒体询问配置路由,大于1224加载PCAPP组件,小于1224之设施加载MobileApp组件

npm install react-responsive --save

class App extends Component {
    render() {
        return (
          <div>
                <MediaQuery query='(min-device-width:1224px)'>
                    <Router history={hashHistory}>
                        <Route path='/' component={PCApp}>
                            <IndexRoute component={PCNewsContainer}/>
                        </Route>
                    </Router>
               </MediaQuery>
               <MediaQuery query='(max-device-width:1224px)'>
                    <Router history={hashHistory}>
                        <Route path='/' component={MobileApp}/>
                    </Router>
               </MediaQuery>
         </div>
        );
    }
}

于POI检索结果页面,地图控件显示也空白,是坐模拟器运行的原因,真机效果佳

3.PC端实现

betway体育 9

头颅及尾巴是持有页面共享的。
中等有些基于路由显示不同之组件。

import React from 'react';
import PCHeader from '../../component/pc/header/pc_header';
import PCFooter from '../../component/pc/footer/pc_footer';

export default class PCApp extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return(
            <div>
                <PCHeader/>
                {this.props.children}
                <PCFooter/>
            </div>
        );
    }
}
这里是 iOS项目地址后端项目地址 。如果有帮衬,希望点转Star坐显示鼓励,感谢~

此是后端代码简析的章后端代码简析

「Tips」:对于发现代码运行报错的同班,项目以CocoaPods进展第三方库的拼。之前为便于我本着Pods文件夹做了ignore操作。刚刚已更新,大家可一直打开后缀为xcworkspace的类文件运行。不过这样Github上面的档次体积会比较坏。

简易介绍:项目UI整体尽量保障了饿了么的蓝色风格,其中一些页面参考了高德地图饿了么Max+的宏图风格。

3.1 头部组件<PCHeader/>的落实

betway体育 10

betway体育 11

export default class PCHeader extends React.Component {
constructor(props) {
        super(props);
        this.state = {
            hasLogined: false,//表示是否登陆
            userName: '', //表示用户名
            userId: '',   //表示用户id
            current: 'top',//表示当前点击的导航
            modalVisable: false, //表示登录注册的模态框是否显示
        };
    }


    //组件加载之前,判断其localstorage是否有值,如果有值,设置state中的用户名和用户id
    //设置登陆状态为true
    //此时显示用户名和退出按钮,即Logout组件
    componentWillMount() {
        //表示存在id
        if (localStorage.userId && localStorage.userId != '') {
            this.setState({userId: localStorage.userId, userName: localStorage.userName, hasLogined: true});
        }
    };
render() {

        return (
            <header>
                <Row>
                    <Col span={2}></Col>
                    <Col span={4}>
                        <a className='logo' href='/'>
                            <img src={logo} alt='logo'/>
                            新闻头条
                        </a>
                    </Col>
                    <Col span={18}>
                       <Nav hasLogined={this.state.hasLogined} logout={this.logout.bind(this)}
                            userName={this.state.userName} current={this.state.current}
                            menuItemClick={this.MenuItemClick.bind(this)}/>
                    </Col>
                </Row>
            </header>
        );
    }

}
![](https://upload-images.jianshu.io/upload_images/8521343-e52055523a23ba64.png)

导航组件根据用户是否登陆来展示(hasLogined),如果登录了(true),最右面显示用户称及撤销登录按钮,如果无登录(false)显示登录以及注册按钮。

betway体育 12

未登录

betway体育 13

已登陆

使用了ant design的menu组件

import React from 'react';
import Logout from './LogoutComponent';
import {Menu, Icon} from 'antd';
import {Link} from 'react-router';
export default class Nav extends React.Component{
    render(){
        //判断用户是否登录,用户登录就显示个人中心和退出按钮
        //用户没有登录就显示注册/登录按钮
        const userShow = this.props.hasLogined ?
            <Menu.Item key="logout">
                <Logout logout={this.props.logout} userName={this.props.userName}/>
            </Menu.Item> :
            <Menu.Item key='register'>
                <Icon type='appstore'/>注册/登录
            </Menu.Item>;

        return(
            <Menu mode="horizontal" selectedKeys={[this.props.current]}
                  onClick={this.props.menuItemClick}>
                <Menu.Item key="top">
                    <Link to='/top'>
                        <Icon type="appstore"/>头条
                    </Link>
                </Menu.Item>

                <Menu.Item key="shehui">
                    <Link to='/shehui'>
                        <Icon type="appstore"/>社会
                    </Link>
                </Menu.Item>

                <Menu.Item key="guonei">
                    <Link to='/guonei'>
                        <Icon type="appstore"/>国内
                    </Link>
                </Menu.Item>

                <Menu.Item key="guoji">
                    <Link to='/guoji'>
                        <Icon type="appstore"/>国际
                    </Link>
                </Menu.Item>

                <Menu.Item key="yule">
                    <Link to='/yule'>
                        <Icon type="appstore"/>娱乐
                    </Link>
                </Menu.Item>

                <Menu.Item key="tiyu">
                    <Link to='/tiyu'>
                        <Icon type="appstore"/>体育
                    </Link>
                </Menu.Item>

                <Menu.Item key="keji">
                    <Link to='/keji'>
                        <Icon type="appstore"/>科技
                    </Link>
                </Menu.Item>

                <Menu.Item key="shishang">
                    <Link to='/shishang'>
                        <Icon type="appstore"/>时尚
                    </Link>
                </Menu.Item>
                {userShow}
            </Menu>

        );
    }
}
![](https://upload-images.jianshu.io/upload_images/8521343-89883669ac279dee.png)

import React from 'react';
import PropTypes from 'prop-types';
import {Button} from 'antd';
//如果已经登录,则头部显示用户名和退出按钮
export default class Logout extends React.Component {
    constructor(props) {
        super(props);
    }


    render() {
        return (
            <div>
                <a href='#' target='_blank'><Button type='primary'>{this.props.userName}</Button></a>
                &nbsp;&nbsp;
                <Button type='ghost' onClick={this.props.logout}>注销用户</Button>
            </div>
        );
    }

}

//设置必须需要userName属性
Logout.propTypes = {
    userName: PropTypes.string.isRequired
};

点击注册登录按钮弹有,注册登录模态框。
在pc_header.js中得相关代码。
Nav组件中为Menu绑定了onClick事件

export default class PCHeader extends React.Component {
....
 MenuItemClick(e) {
        //注册登录MenuItem点击后,设置current值,显示注册登录的模态框
        if (e.key === 'register') {
            //高亮显示当前点击的MenuItem
            this.setState({current: 'register'});
            //显示模态框
            this.setModalVisible(true);
        } else {
            this.setState({current: e.key});
        }
    }

    //设置注册和登录模态框是否显示,默认不显示
    setModalVisible(value) {
        this.setState({modalVisable: value});
    }
}
//return中添加模态框组件
return(){
    ...
 <Col span={18}>
...
 <LoginRegisterModal setModalVisible={this.setModalVisible.bind(this)} login={this.login.bind(this)} visible={this.state.modalVisable}/>
 </Col>
}
![](https://upload-images.jianshu.io/upload_images/8521343-8b693c33a63686fe.png)



Modal组件中嵌套了Tabs组件,登录tab和注册tab

import React from 'react';
import {Tabs, Modal} from 'antd';
import WrappedRegisterForm from './RegisterForm'
import WrappedLoginForm from './LoginForm'

export default class LoginRegisterModal extends React.Component {
    handleCancel(){
        this.props.setModalVisible(false);
    }

    render(){

        return(

            <Modal title="用户中心" visible={this.props.visible}
                   onCancel={this.handleCancel.bind(this)}
                   onOk={this.handleCancel.bind(this)}>
                <Tabs type="card">
                    <Tabs.TabPane tab='登录' key='1'>
                        <WrappedLoginForm login={this.props.login} setModalVisible={this.props.setModalVisible}/>
                    </Tabs.TabPane>
                    <Tabs.TabPane tab='注册' key='2'>
                        <WrappedRegisterForm setModalVisible={this.props.setModalVisible}/>
                    </Tabs.TabPane>

                </Tabs>
            </Modal>
        );
    }
}

import React from 'react';
import {Icon, message,  Form, Input, Button} from 'antd';
//注册表单组件
class RegisterForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {confirmDirty: false};
    }

    //处理注册提交表单
    handleRegisterSubmit(e) {
        //页面开始向API进行提交数据
        //阻止submit事件的默认行为
        e.preventDefault();

        this.props.form.validateFields((err, formData) => {
            if (!err) {
                console.log('Received values of form: ', formData);
                let myFetchOptions = {method: 'GET'};
                //发起注册数据请求
                fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=register&username=" + formData.userName + "&password=" + formData.password + "&r_userName=" + formData.r_userName + "&r_password=" + formData.r_password + "&r_confirmPassword=" + formData.r_confirmPassword, myFetchOptions)
                    .then(response => response.json()).then(json => {
                    if (json) {
                        message.success("注册成功");
                        //设置模态框消失
                        this.props.setModalVisible(false);
                    }

                });


            }
        })
    }

    //注册验证确认密码框输入的密码两次是否一样
    checkPassword(rule, value, callback) {
        const form = this.props.form;
        if (value && value !== form.getFieldValue('r_password')) {
            callback('两次输入的密码不一致!');
        } else {
            callback();
        }
    }

    //注册检验密码
    checkConfirm(rule, value, callback) {
        const form = this.props.form;
        if (value && this.state.confirmDirty) {
            form.validateFields(['r_confirmPassword'], {force: true});
        }
        callback();
    }

    render() {
        let {getFieldDecorator} = this.props.form;
        return (
            <Form onSubmit={this.handleRegisterSubmit.bind(this)}>
                <Form.Item lable="账户">
                    {getFieldDecorator('r_userName', {
                        rules: [{required: true, message: '请输入您的账户!'}],
                    })
                    (<Input
                        prefix={<Icon type="user" style={{color: 'rgba(0,0,0,.25)'}}/>}
                        placeholder='请输入您的账户'/>)}
                </Form.Item>

                <Form.Item lable="密码">
                    {getFieldDecorator('r_password', {
                        rules: [{required: true, message: '请输入您的密码'}, {
                            validator: this.checkConfirm.bind(this),
                        }],
                    })(
                        <Input prefix={<Icon type="lock"
                                             style={{color: 'rgba(0,0,0,.25)'}}/>}
                               type='password' placeholder='请输入您的密码'/>)}
                </Form.Item>

                <Form.Item lable="确认密码">
                    {getFieldDecorator('r_confirmPassword', {
                        rules: [{
                            required: true, message: '请确认您的密码!',
                        }, {
                            validator: this.checkPassword.bind(this),
                        }],
                    })(
                        <Input prefix={<Icon type="lock"
                                             style={{color: 'rgba(0,0,0,.25)'}}/>}
                               type='password' placeholder='请再次输入您的密码'/>
                    )}
                </Form.Item>

                <Form.Item>
                    <Button type='primary' htmlType='submit'>注册</Button>
                </Form.Item>
            </Form>
        );
    }
}

const WrappedRegisterForm = Form.create()(RegisterForm);

export default WrappedRegisterForm;

登录tab中封装了登录表单
<WrappedLoginForm/>组件

import React from 'react';
import {Icon,Form, Input, Button,Checkbox} from 'antd';
import './pc_header.css'
//登录表单组件
class LoginForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {hasUser: ''};
    }

    //motal框中的处理登录提交表单
    handleLoginSubmit(e) {
        //页面开始向API进行提交数据
        //阻止submit事件的默认行为
        e.preventDefault();
        this.props.form.validateFields((err, formData) => {
            if (!err) {
                console.log('Received values of form: ', formData);
                let myFetchOptions = {method: 'GET'};
                fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=login&username=" + formData.userName + "&password=" + formData.password + "&r_userName=" + formData.r_userName + "&r_password=" + formData.r_password + "&r_confirmPassword=" + formData.r_confirmPassword, myFetchOptions)
                    .then(response => response.json())
                    .then(json => {
                        if (json !== null) {
                            console.log(json);
                            let userLogin = {userName: json.NickUserName, userId: json.UserId};
                            this.props.login(userLogin);
                            //设置模态框消失
                            this.props.setModalVisible(false);
                        }
                        else {
                            //如果json为null,表示用户名密码不存在
                            this.setState({hasUser: '用户名或密码错误'});
                        }

                    });


            }
        });
    }

    render() {
        let {getFieldDecorator} = this.props.form;
        return (
            <Form onSubmit={this.handleLoginSubmit.bind(this)}>
                <Form.Item>
                    {getFieldDecorator('userName', {
                        rules: [{
                            required: true,
                            message: 'Please input your username!'
                        }],
                    })(
                        <Input prefix={<Icon type="user"
                                             style={{color: 'rgba(0,0,0,.25)'}}/>}
                               placeholder="Username"/>
                    )}
                </Form.Item>
                <Form.Item>
                    {getFieldDecorator('password', {
                        rules: [{
                            required: true,
                            message: 'Please input your Password!'
                        }],
                    })(
                        <Input prefix={<Icon type="lock"
                                             style={{color: 'rgba(0,0,0,.25)'}}/>}
                               type="password" placeholder="Password"/>
                    )}
                </Form.Item>
                <Form.Item>
                    {getFieldDecorator('remember', {
                        valuePropName: 'checked',
                        initialValue: true,
                    })(
                        <Checkbox>Remember me</Checkbox>
                    )}
                    {this.state.hasUser}
                    <Button type="primary" htmlType="submit"
                            className="login-form-button">
                        Log in
                    </Button>

                </Form.Item>
            </Form>
        );
    }

}

const WrappedLoginForm = Form.create()(LoginForm);
export default WrappedLoginForm;

内部涉嫌到的login(userLogin)方法,在header.js中

  //点击登录表单中的登录按钮,直接设置为登录状态
    login(userLogin) {
        this.setState({userName: userLogin.userName, hasLogined: true, userId: userLogin.userId});
        localStorage.userName = userLogin.userName;
        localStorage.userId = userLogin.userId;
    }

Logout组件中之取消登陆按钮绑定了logout事件
header.js中logout事件

 //点击MenuItem中退出登录按钮
    logout() {
        localStorage.userName = '';
        localStorage.userId = '';
        this.setState({hasLogined: false, userName: '', userId: ''});
    };

种效益点

3.2 头长达首页内容区

betway体育 14

betway体育 15

左边空2列,中间21列,右边1列
中间21列,左边8列,中间10列,右边6列
pc_news_container.js

betway体育 16

路下及之API及第三方库

3.2.1 左边有实现

pc_news_container中上手架构。

 <Row>
       <Col span={2}/>

       <Col span={21}>
            <Row className='top_news'>
                   <Col span={8}>
                         <div className='top_left'>
                                <Carousel autoplay>
                                    <div><img src={img1}/></div>
                                    <div><img src={img2}/></div>
                                    <div><img src={img3}/></div>
                                    <div><img src={img4}/></div>
                                </Carousel>
                               <PCNewsImageBlock count={6} type='guoji' width='100%'  cartTitle='国际新闻' justifyContent='space-around' imageWidth='112px' />
                         </div>
                   </Col>
                   <Col span={10}></Col>
                   <Col span={6}></Col>
              </Row>
      </Col>

      <Col span={1}/>

里头Carousel是轮播图插件。

![](https://upload-images.jianshu.io/upload_images/8521343-e132c2583f1f6f9e.png)

betway体育 17

通向后台发起呼吁,得到json数据,赋给this.state.news。
传送给<ImageNewsComponent/>组件。

import React from 'react';
import ImageNewsComponent from './image_news_component';

export default class PCNewsImageBlock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {news: ''};
    }

    componentDidMount() {
        let fetchOption = {method: 'Get'};
        fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnews&type=" + this.props.type + "&count=" + this.props.count, fetchOption).then(response => response.json()).then(json => this.setState({news: json}));
    }

    render() {
        const news = this.state.news;
        let newsImage = news.length ?
            <ImageNewsComponent news={news} imageWidth={this.props.imageWidth} cartTitle={this.props.cartTitle} justifyContent={this.props.justifyContent}/>
            : '正在加载';
        return (
            <div>{newsImage}</div>
        );
    }
}

import React from 'react';
import {Card} from 'antd';
import {Link} from 'react-router';
import './image_news_component.css';

export default class ImageNewsComponent extends React.Component {
    render(){
        const news=this.props.news;
        const newsList=news.map((newsItem, index) => (
            <div key={index} className='image_news_item' style={{width:this.props.imageWidth}}>
                <Link to={`details/${newsItem.uniquekey}`} target='_blank'>
                    <img alt="newsItem.title" src={newsItem.thumbnail_pic_s} width={this.props.imageWidth}/>
                    <h3>{newsItem.title}</h3>
                    <p>{newsItem.author_name}</p>
                </Link>
            </div>
        ));

        return(
            <Card className='image_card' title={this.props.cartTitle} bordered={true} style={{width: this.props.width,marginTop:'10px'}}>
                <div className='image_news_container' style={{width: this.props.width,justifyContent:this.props.justifyContent}}>
                    {newsList}
                </div>
            </Card>

        );
    }
}

image_news_container采用flexbox布局,对齐方式由父组件传递而来,这里调用时传递的是center。
样式:

.image_news_container{
    display:flex;
    flex-wrap:wrap;
}

/*图片框每个item的div*/
.image_news_item{
    margin: 0.5rem;
}

/*设置图片块每一项的标题*/
.image_news_item h3{
    white-space: nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
}
/*左边imageblock的内边距*/
.image_card .ant-card-body{
    padding-left: 0px;
    padding-right: 0px;
    padding-bottom: 14px;
}

类事关的技术点

3.2.2 中间有些的落实

pc_news_container.js

 <Col span={10}>
      <div className='top_center'>
           <Tabs defaultActiveKey="1">
                 <Tabs.TabPane tab='头条新闻' key='1'>
                     <PCNewsBlock count={30} type='top' width='100%' bordered='false'/>
                </Tabs.TabPane>
           </Tabs>
     </div>
 </Col>
![](https://upload-images.jianshu.io/upload_images/8521343-7a3fe3ac22264c00.png)



![](https://upload-images.jianshu.io/upload_images/8521343-c896c66a36e2a33a.png)

import React from 'react';
import PCNewsComponent from './pc_news_Component';

export default class PCNewsBlock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {news: ''};
    }

    //页面渲染后触发
    componentDidMount() {
        let fetchOption = {method: 'GET'};
        fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnews&type=" + this.props.type + "&count=" + this.props.count, fetchOption).then(response => response.json()).then(json => this.setState({news: json}));
    }


    render() {
        const news = this.state.news;
        //看news的长度是否为0,字符串长度为0则是false表示未加载到数据,为其他值则true加载到数据
        const newsCard = news.length ?
            <PCNewsComponent news={news}/>
            : '正在加载';

        return (
            <div>
                {newsCard}
            </div>

        );
    }
}

import {Link} from 'react-router';
import {Card} from 'antd';
import React from 'react';
import './pc_news_component.css'
export default class NewsComponent extends React.Component {
    constructor(props){
        super(props);
    }
    render(){
        let news=this.props.news;
        let newsList=news.map((newsItem, index) => (
            <li key={index}>
                <Link to={`details/${newsItem.uniquekey}`} target='_blank'>
                    {newsItem.title}
                </Link>
            </li>
        ));

        return (
            <Card className='news_card'>
                <ul>
                    {newsList}
                </ul>
            </Card>

        );
    }
}

开思路

betway体育 18

IMG_2008.PNG

betway体育 19

IMG_2009.PNG

betway体育 20

IMG_2010.PNG

betway体育 21

IMG_2011.PNG

//判断滑动手势方向,决定tableview的frame改变
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
  CGPoint translation = [scrollView.panGestureRecognizer translationInView:scrollView.superview];
  if (translation.y>0) {
      NSLog(@"ssssss");
      {
          NSIndexPath *dic = self.poiList.indexPathsForVisibleRows.firstObject;
          if (self.poiList.frame.origin.y == 64 && dic.row == 0){
              [UIView animateWithDuration:0.2 animations:^{

                  NSLog(@"visible:::%@",dic);
                  showMap = YES;
                  self.navigationController.navigationBar.hidden = YES;
                  self.poiList.frame = listFrame;
              }];
          }
      }
  }else if(translation.y<0){
      NSLog(@"tttt");
      if (self.poiList.frame.origin.y == 300){
          [UIView animateWithDuration:0.2 animations:^{
              showMap = NO;
              self.navigationController.navigationBar.hidden = NO;
              self.poiList.frame = listFullFrame;
          }];
      }
  }
}

betway体育 25

IMG_2014.PNG

betway体育 26

IMG_2015.PNG

{
[super becomeFirstResponder];
return [self.textInput becomeFirstResponder];
}
“`

betway体育 27

IMG_2016.PNG

betway体育 28

IMG_2017.PNG

betway体育 29

IMG_2019.PNG


这片为依旧采取了打包的loading页面。如果数额条目为空,则免显得tableview,提示暂无相关信息,如图。

代码的重要性出思路基本描述了,具体的底细大家可以同本人连续交流~项目受到有无数不尽合理的兑现方式,小片是考虑到时间因素。欢迎大家进行讨论、指教。
生连锁的题目,欢迎大家进行留言。项目蒙采取的起来接口,部署于自家之阿里云服务器被,可外网访问。请求大家合理运用。

后端项目数据库相关的sql代码,我就达到传至后端项目

苟大家来趣味,以后本人可重复讲述一下server掬的代码,当然,后端我也是初专家的品位,仅供想要入门的同班参考。

本身之个人博客网站地址:
Halo的个体博客
,欢迎大家拜。

代码都开源,地址在篇章首部。如果接触转star,我真正会非常感谢~
halo

3.2.3 右边有实现

betway体育 30

pc_news_container

<Col span={6}>
    <div className='top_right'>
      <PCNewsImageSingle width='100%' ImageWidth='100px' type='shehui' count={6} title='社会新闻'/>
    </div>
 </Col>

betway体育 31

import React from 'react';
import ImageSingleComponent from './imageSingle_component'

 export default class PCNewsImageSingle extends React.Component{
    constructor(props) {
        super(props);
        this.state = {news: ''};
    }

    //页面渲染之前
    componentDidMount() {
        let fetchOption = {method: 'GET'};
        fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnews&type=" + this.props.type + "&count=" + this.props.count, fetchOption).then(response => response.json()).then(json => this.setState({news: json}));
    }

    render(){
        const news=this.state.news;
        const newsList=news.length?
          <ImageSingleComponent news={news} ImageWidth={this.props.ImageWidth} width={this.props.width} title={this.props.title}/>
            :'正在加载';

        return(
            <div >
                {newsList}
            </div>
        );
    }
}

import React from 'react';
import {Link} from 'react-router';
import {Card} from 'antd';
import './imageSingle_component.css';
export default class ImageSingleNewComponent extends React.Component{
    render(){

        const news=this.props.news;
        const newsList=news.map((newsItem,index)=>(
            <Link to={`details/${newsItem.uniquekey}`} target='_blank' key={index}>
                <section  className='imageSingle_sec' style={{width:this.props.width}}>
                    <div className='imageSingle_left' style={{width:this.props.ImageWidth}}>
                        <img style={{width:this.props.ImageWidth}} src={newsItem.thumbnail_pic_s} alt={newsItem.title}/>
                    </div>

                    <div className='imageSingle_right'>
                        <p>{newsItem.title}</p>
                        {newsItem.realtype}
                        {newsItem.author_name}
                    </div>
                </section>
            </Link>

        ));

        return(
            <Card title={this.props.title} className='imageSingleCard'>
                {newsList}
            </Card>

        );
    }
}

css

.imageSingle_sec {
    border-bottom: thin #E8E8E8 solid;
    display: flex;
    align-items: center;
    box-sizing: content-box;
    height: 95px;
    padding: 5px 0;

}

.imageSingle_sec .imageSingle_right {
    margin-left:1em;
    flex:1;
}

.imageSingle_sec .imageSingle_right p{
   margin-bottom:0;
}

.imageSingle_sec .imageSingle_right .realType{
    color:red;
    font-weight:bolder;
    margin-right:1em;
}

.imageSingleCard{
    height: 736px;
}

3.2.4 最下有

betway体育 32

 <Row>
    <PCNewsImageBlock count={16} type='guonei' width='100%' imageWidth='112px' cartTitle='国内新闻'  justifyContent='space-start'/>
    <PCNewsImageBlock count={16} type='yule' width='100%' imageWidth='112px' cartTitle='娱乐新闻' justifyContent='space-start'/>
</Row>

3.3.详情页

betway体育 33

betway体育 34

import React from 'react';
import {Row, Col} from 'antd';
import PCNewsImageBlock from '../../component/pc/topcontent/pc_news_image/pc_news_imageblock';
import Comment from '../../component/common/common_comment';

export default class PCNewsDetail extends React.Component {
    constructor() {
        super();
        this.state = {
            newsItem: ''
        };
    }

    componentDidMount() {
        let fetchOption = {
            method: 'GET'
        };
        fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnewsitem&uniquekey=" + this.props.params.uniquekey, fetchOption)
            .then(response => response.json())
            .then(json => {
                this.setState({newsItem: json});
                document.title = this.state.newsItem.title + "-新闻头条";
            });
    }

    createMarkup() {
        return {__html: this.state.newsItem.pagecontent};
    }

    render() {
        return (

            <div>
                <Row>
                    <Col span={2}/>
                    <Col span={14}>
                        <div style={{marginTop: '50px'}} dangerouslySetInnerHTML={this.createMarkup()}/>
                        <Comment uniquekey={this.props.params.uniquekey}/>
                    </Col>
                    <Col span={1}/>
                    <Col span={6}>
                        <PCNewsImageBlock imageWidth='150px' width='100%'  count={40} type='top' cartTitle='推荐'/>
                    </Col>
                    <Col span={1}/>
                </Row>
            </div>

        );
    }
}

3.4.页脚

betway体育 35

import React from 'react';
import {Row,Col} from 'antd';


export default class PCFooter extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return (
            <footer>
                <Row>
                    <Col span={2}/>
                    <Col span={20} style={{ textAlign:'center'}}>
                        &copy;&nbsp;2018 新闻头条。All Rights Reserved.
                    </Col>
                    <Col span={2}/>
                </Row>
            </footer>
        );
    }
}

4.手机端实现

betway体育 36

betway体育 37

mobile_app.js

import React from 'react';
import MobileHeader from '../../component/mobile/header/mobile_header';
import MobileFooter from '../../component/mobile/footer/mobile_footer';
import MobileContent from '../../component/mobile/content/mobile_content';


export default class MobileApp extends React.Component {
    render() {
        return (
            <div>
                <MobileHeader/>
                <MobileContent/>
                <MobileFooter/>
            </div>
        );
    }
}

4.1 头部实现

思路与pc端类似,点击icon显示登陆框,如果就报到,点击即取消。

export default class MobileHeader extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            current: 'top',
            hasLogined: false,
            modalVisable: false,
            userName: '',
        };
    }

    setModalVisible(value) {
        this.setState({modalVisable: value});
    }

    handleClick() {
        this.setModalVisible(true);
    }

    //组件加载之前,判断其localstorage是否有值
    componentWillMount(){
        //表示存在id
        if (localStorage.userId&&localStorage.userId!='') {
            this.setState({userId:localStorage.userId,userName:localStorage.userName,hasLogined:true});
        }
    };

    //点击登录按钮
    login(userLogin){
        this.setState({userName:userLogin.userName,hasLogined:true,userId:userLogin.userId});
        localStorage.userName=userLogin.userName;
        localStorage.userId=userLogin.userId;
    }

    logout() {
        localStorage.userName = '';
        localStorage.userId = '';
        this.setState({hasLogined: false, userName: '', userId: ''});
    };

    render() {
        const userShow = this.state.hasLogined ?
            <Icon type='inbox' onClick={this.logout.bind(this)}/> : <Icon type='setting' onClick={this.handleClick.bind(this)}/>;
        return (
            <div id="mobile">
                <header>
                    <Link to='/'><img src={Logo} alt="mobile_logo"/></Link>
                    新闻头条
                    {userShow}
                </header>
                <LoginRegisterModal setModalVisible={this.setModalVisible.bind(this)} login={this.login.bind(this)}
                                    visible={this.state.modalVisable}/>
            </div>
        );
    }
}

4.2 内容区

import React from 'react';
import img1 from '../../../staticrousel_1.jpg';
import img2 from '../../../staticrousel_2.jpg';
import img3 from '../../../staticrousel_3.jpg';
import img4 from '../../../staticrousel_4.jpg';
import {Tabs,Carousel} from 'antd';
import MobileNews from '../../../component/mobile/content/mobile_news';
export default class MobileContent extends React.Component {
    render() {

      return(
          <Tabs>
              <Tabs.TabPane tab='头条' key='1'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>

                  <MobileNews count={50} type='top' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>

              <Tabs.TabPane tab='国内' key='3'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='guonei' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>
              <Tabs.TabPane tab='国际' key='4'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='guoji' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>
              <Tabs.TabPane tab='娱乐' key='5'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='yule' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>

              <Tabs.TabPane tab='社会' key='6'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='shehui' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>

              <Tabs.TabPane tab='体育' key='7'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='tiyu' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>

              <Tabs.TabPane tab='科技' key='8'>
                  <div>
                      <Carousel autoplay>
                          <div><img src={img1}/></div>
                          <div><img src={img2}/></div>
                          <div><img src={img3}/></div>
                          <div><img src={img4}/></div>
                      </Carousel>
                  </div>
                  <MobileNews  type='keji' ImageWidth='112px' width='100%'/>
              </Tabs.TabPane>

          </Tabs>
      );
    }
}

import React from 'react';

import MobileNewsComponent from './mobile_news_component'
import LoadMore from './LoadMore'

export default class MobileNews extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            news: [],
        };
    }

    componentDidMount() {
        let fetchOption = {method: 'GET'};
        fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnews&type=" + this.props.type + "&count=" + this.state.count, fetchOption)
            .then(response => response.json())
            .then(json => this.setState({news: json}));
    }

    render() {
        const news = this.state.news;

        const newsList = news.length ?
           <MobileNewsComponent news={news} ImageWidth={this.props.ImageWidth}/>
            : '正在加载';

        return (
            <div className='mobile_news'>
                {newsList}
            </div>
        );
    }
}

import React from 'react';
import {Link} from 'react-router';
import './mobile_news_component.css'
export default class MobileNewsComponent extends React.Component{


    render(){

        const newlist=this.props.news.map((newsItem, index) => (
            <Link to={`details/${newsItem.uniquekey}`} target='_blank' key={index}>
               <section  className='mob_news_sec'>

                    <div style={{width:this.props.ImageWidth}}>
                        <img src={newsItem.thumbnail_pic_s} alt={newsItem.title} style={{width:this.props.ImageWidth}}/>
                    </div>

                    <div className='mob_news_right'>
                        <h3>{newsItem.title}</h3>
                        {newsItem.realtype}
                        {newsItem.author_name}
                    </div>
                </section>
            </Link>

        ));
        return(
            <div>
            {newlist}

            </div>
        );
    }
}

css样式

.mob_news_sec{
    border:thin #E8E8E8 solid;
    padding:0.5rem 0;
    display: flex;
    align-items: center;
    height: 90px;
    box-sizing: content-box;
}

.mob_news_sec .mob_news_right{
    flex: 1;
    margin-left:0.5rem ;
}

.mob_news_sec .mob_news_right .mob_news_realtype{
    color:red;
    font-weight:bolder;
    margin-right: 1em;

}

export default class MobileNews extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            news: [],
            count: 10,
            isLoading:false,
            hasMore:true,
        };
    }

//加载更多方法
    loadMoreFn(){

        this.setState({isLoading:true});
        let count=this.state.count+10;
        if (count>0&&count<300){
            this.setState({count:count});
            let fetchOption = {method: 'GET'};
            fetch("http://newsapi.gugujiankong.com/Handler.ashx?action=getnews&type=" + this.props.type + "&count=" + this.state.count, fetchOption)
                .then(response => response.json())
                .then(json => this.setState({news: json}));
            this.setState({isLoadingMore: false});
        }else {
            this.setState({isLoading:false, hasMore:false})
        }
    }

       ...
        return (
            <div className='mobile_news'>
                {newsList}
                {
                    this.state.hasMore?
                    <LoadMore isLoading={this.state.isLoading} loadMoreFn={this.loadMoreFn.bind(this)}/>
                    :<div style={{textAlign:'center',backgroundColor:'#F8F8F8'}}>木有更多咯</div>
                }

            </div>
        );

LoadMore组件

import React from 'react';
export default class LoadMore extends React.Component{
    constructor(props){
        super(props);
    }

    handleClick(){
        this.props.loadMoreFn();
    }

    componentDidMount(){
        const loadMoreFn=this.props.loadMoreFn;
        const wrapper=this.refs.wrapper;
        let timeoutId;
        function callback(){
            //得到加载更多div距离顶部的距离
           let top=wrapper.getBoundingClientRect().top;
           let windowHeight=window.screen.height;
           //如果top距离比屏幕距离小,说明加载更多被暴露
           if(top&&top<windowHeight)
               loadMoreFn();
        }
        //添加滚动事件监听
        window.addEventListener('scroll',function () {
            if(this.props.isLoadingMore)
                return;
            if(timeoutId)
                clearTimeout(timeoutId);
            //因为一滚动就会触发事件,我们希望50ms才触发一次
            timeoutId=setTimeout(callback,50);
        }.bind(this),false);
    }


    render(){
        return(
            <div ref='wrapper' style={{textAlign:'center',backgroundColor:'#F8F8F8'}}>
                {
                    this.props.isLoadingMore?
                        加载中...
                        :加载更多
                }
            </div>
        );
    }
}

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图