完成详情页渲染,用到了react-markdown来渲染
先看效果:

效果

1 在components下新建Detail.js

import React , { Component }from 'react';
import { Card, Avatar, Spin, Icon,Comment, Tooltip, List} from 'antd';
import PropTypes from 'prop-types';
import { connect } from 'dva';
import  ReactMarkdown from 'react-markdown';
import './my.css';


class Detail extends Component{

    render() {
       const { Meta } = Card;
      return (
        <Spin spinning={this.props.loading.global}
        size='large'
        tip="数据正在加载中">
    {typeof(this.props.data.author)=='object' ? (
      <Card>
      <Card
     type="inner"
     title={<div><Avatar src={this.props.data.author.avatar_url} />
     &nbsp;&nbsp;&nbsp;
     <span style={{fontSize :26}}>{this.props.data.title}</span>
     </div>}
     extra={
       <div>
        发布于{this.props.data.create_at} *作者{this.props.data.author.loginname}*  {this.props.data.visit_count}次浏览  *来自 分享
       </div>
     }
     >
      <ReactMarkdown source={this.props.data.content} />
   </Card>
   <Card
   type="inner"
   title={this.props.data.reply_count+'个回复'}>

   <List
    className="comment-list"
    itemLayout="horizontal"
    dataSource={this.props.data.replies}
    renderItem={item => (
      <Comment
        author={item.author.loginname}
        avatar={item.author.avatar_url}
        content={<ReactMarkdown source={item.content} />}
        datetime={item.create_at}
      />
    )}
  />

   </Card>
    </Card>
    ) : ''}

 </Spin>
      )

    }

    componentWillMount () {
        const { par } = this.props
         this.props.dispatch({ type: 'detail/find', payload: { id:par} })
   }
}


Detail.propTypes = {
    id: PropTypes.string.isRequired,
};

function mapStateToProps(state) {
  const {id,data} = state.detail;
  return {
      id,
      data,
      loading:state.loading
  };
}

// export default ListData;
export default connect(mapStateToProps)(Detail);

用到了antd中的一些组件,可以自己去官网参考下怎么用。 my.css里面定义了一些样式,主要解决markdown渲染后里面的图片太宽,重新设置宽度。


a{
    text-decoration:none;
    color:#333;
}

img{
  max-width: 1300px;
}

2 在models下创建对应的model detail.js

import * as listService from '../services/list';
export default {
  namespace: 'detail',
  state:{
      id:'',
      data:{}
  },
    effects: {
        *find({ payload: { id } }, { call, put }) {
            const result = yield call(listService.find, { id })
                yield put({
                type: 'updateData',
                payload: {
                    result,
                    id
                }
            })
        }
    },
  reducers: {
    'updateData'(state, { payload: data }) {
            let r = data.result.data
            const {id} = data
        return {...state,id,data:r}
    }
  },
    subscriptions : {
        setup({ dispatch, history }) {

    }
},
};

3 在service中添加获取详情的api list.js

import request from '../utils/request';

export function query({ page,pageSize,type }) {
  return request(`/api/v1/topics?page=${page}&limit=${pageSize}&tab=${type}`);
}

export function find({ id }) {
  return request(`/v1/topic/${id}?mdrender=false`);
}

mdrender参数设置为false来获取markdown数据,truehtml数据

4 创建详情页routes/DetailPage.js

import React from 'react';
import { connect } from 'dva';
import Header from '../components/Header';
import Detail from '../components/Detail';

function DetailPage(props) {
 const {params} = props.match
  return (
    <div>
      <Header keys={['index']}/>
      <div style={{paddingTop:20,paddingLeft:100,paddingRight:100,paddingBottom:50}}>
          <Detail par={params.id}/>
      </div>
    </div>
  );
}

DetailPage.propTypes = {
};

export default connect()(DetailPage);

使用了自己定义的HeaderDetail组件。在router.js中邦定路由到页面:

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import DetailPage from './routes/DetailPage';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
        <Route path="/detail/:id" exact component={DetailPage} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;

使用了参数路由,在DetailPage.js中,从props.match.params中就可以取到id值传给Detail组件

5 别忘了在index.js中注册model以及插件

import dva from 'dva';
import './index.css';
import createLoading from 'dva-loading';

// 1. Initialize
const app = dva();

// 2. Plugins
 app.use(createLoading());

// 3. Model
app.model(require('./models/listdata').default);
app.model(require('./models/detail').default);

// 4. Router
app.router(require('./router').default);

// 5. Start
app.start('#root');

前面两节课忘说dva-loading了,需要在这里使用,才能在组件中获取loading属性

6 在ListData组件中加入路由跳转详情页

import {Link} from 'dva/router';
<Link to={'/detail/'+item.id}>{item.title}</Link>

使用了Link来做跳转,顺带把我们的Header组件的跳转也给改了

 <Menu.Item key="index">
       <Link to="/"><Icon type="appstore" />首页</Link>
       </Menu.Item>
       <Menu.Item key="into">
         <Link to="/into"><Icon type="appstore" />新手入门</Link>
       </Menu.Item>
       <Menu.Item key="api">
         <Link to="/api"><Icon type="appstore" />API</Link>
       </Menu.Item>
       <Menu.Item key="about">
        <Link to="/about"><Icon type="appstore" />关于</Link>
       </Menu.Item>
       <Menu.Item key="reg">
         <Link to="/reg"><Icon type="appstore" />注册</Link>
       </Menu.Item>
       <Menu.Item key="login">
        <Link to="/login"><Icon type="appstore" />登陆</Link>
       </Menu.Item>

大功告成看看效果

成果

欢迎关注我的公众号mike啥都想搞,学习更多内容。
mike啥都想搞


Mike晓
95 声望18 粉丝