ThoughtWorkreactreact.js

11.React实战开发--适配PC及移动端的新闻头条网站

2018-03-10  本文已影响1073人  Ching_Lee

github:https://github.com/Ching-Lee/react_news

1.环境搭建

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

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

create-react-app react-news


进入项目目录

cd react-news
npm start

3000端口

清除App.js中的内容

import React, { Component } from 'react';

import './App.css';

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

export default App;
1.2 引入Ant Design的UI框架

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

npm install --save antd

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;
}
....

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>
        );
    }
}

3.PC端实现


头部和尾部是所有页面共享的。
中间部分根据路由显示不同的组件。

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>
        );
    }
}

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


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'/>
                            <span>新闻头条</span>
                        </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>
        );
    }

}

导航组件根据用户是否登陆来显示(hasLogined),如果登录了(true),最右侧显示用户名和注销登录按钮,如果没有登录(false)显示登录和注册按钮。


未登录
已登陆

使用了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>

        );
    }
}
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>
}
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>
                    )}
                    <span>{this.state.hasUser}</span>
                    <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 头条首页内容区



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


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是轮播图插件。


向后台发起请求,得到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>
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>

        );
    }
}

3.2.3 右边部分实现


pc_news_container

<Col span={6}>
    <div className='top_right'>
      <PCNewsImageSingle width='100%' ImageWidth='100px' type='shehui' count={6} title='社会新闻'/>
    </div>
 </Col>
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>
                        <span className='realType' >{newsItem.realtype}</span>
                        <span>{newsItem.author_name}</span>
                    </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 最下面部分

 <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.详情页


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.页脚

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.手机端实现



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>
                    <span>新闻头条</span>
                    {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 '../../../static/images/carousel_1.jpg';
import img2 from '../../../static/images/carousel_2.jpg';
import img3 from '../../../static/images/carousel_3.jpg';
import img4 from '../../../static/images/carousel_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>
                        <span className='mob_news_realtype'>{newsItem.realtype}</span>
                        <span>{newsItem.author_name}</span>
                    </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?
                        <span>加载中...</span>
                        :<span onClick={this.handleClick.bind(this)}>加载更多</span>
                }
            </div>
        );
    }
}
上一篇下一篇

猜你喜欢

热点阅读