Vue.js入门 0x13 实战:知乎日报项目开发-文章详情页

本文详细介绍如何在Vue项目中使用组件动态加载文章内容及评论,实现文章ID变化时自动请求新文章,同时展示了时间戳转相对时间的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

加载内容

    右侧的文章内容区域封装成了一个组件。在components目录下新建daily-article.vue组件,它唯一接收唯一的一个prop:id,也就是文章的id,如果id变化了,就说明切换了文章,需要请求新的文章内容。

    在app.vue中导入daily-article.vue组件,并在文章列表的Item组件上绑定查看文章事件

//app.vue
<template>
    <div class="daily">
        <div class="daily-menu">
            
        </div>
        <div class="daily-list" ref="list">
            <template v-if="tyep==='recommend'">
                <div v-for="list in recommendList">
                    <div class="daily-date">{{formatDay(list.date)}}</div>
                    <Item
                       @click.native="handleClick(time.id)"
                        v-for="item in list.stories"
                        :data="item"
                        :key="item.id"></Item>
                </div>
            </template>
            <template v-if="type==='daily'">
                <Item
                   @click.native="handleClick(time.id)"
                    v-for="item in list"
                    :data="item"
                    :key="item.id"></Item>
            </template>
        </div>
        <daily-article :id="articleId"></daily-article>
    </div>
</template>
<script>
    import $ from './libs/util';
    import Item from './components/item.vue';
    import dailyArticle from './components/daily-article.vue';
    export default{
        components:{Item,dailyArticle},
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                recommendList:[],
                list:[],
                dailyTime:$.getTodayTime(),
                isLoading:false,
                articleId:0
            }
        },
        methods:{
            
            handleClick(id){
                this.articleId = id;
            }
        }
    }
</script>

    Item是组件,绑定原生事件时要带事件修饰符.native,否则会认为监听的是来自Item组件的定义事件click。

    dailyArticle组件在监听到id改变时请求文章内容:

//components/daily-article.vue
<template>
    <div class="daily-article">
        <div class="daily-article-title">{{data.title}}</div>
        <div class="daily-article-content" v-html="data.body"></div>
    </div>
</template>
<script>
    import $ from '../libs/util';
    export default{
        props:{
            id:{
                type:Number,
                default:0
            }
        },
        data(){
            return {
                data:{}
            }
        },
        methods:{
            getArticle(){
                $.ajax.get('new/'+this.id).then(res=>{
                    //将文章中的图片地址替换为代理的地址
                    res.body = res.body.replace(/src="http/g,'src="'+$.imgPath+'http');
                    res.body = res.body.replace(/src="https/g,'src="'+$.imgPath+'https');
                    this.data = res;
                    //返回文章顶部
                    window.scrollTo(0,0);
                })
            }
        },
        watch:{
            id(val){
                if(val)this.getArticle();
            }
        }
    };
</script>

//style.css

.daily-article{
    margin-left: 450px;
    padding: 20px;
}
.daily-article-title{
    font-size: 28px;
    font-weight: bold;
    color: #222;
    padding: 10px 0;
}
.view-more a{
    display: block;
    cursor: pointer;
    background: #f5f7f9;
    text-align: center;
    color: inherit;
    text-decoration: none;
    padding: 4px 0;
    border-radius: 3px;
}

加载评论

    每条评论要显示发表时间,源数据格式为时间戳,需要前端转为相对时间。在daily目录下创建directives目录,并创建time.js文件

//directives/time.js
var Time = {
    //获取当前时间戳
    getUnix:function(){
        var date = new Date();
        return date.getTime();
    },
    //获取今天0点0分0秒的时间戳
    getTodayUnix:function(){
        var date = new Date();
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date.getTime();
    },
    //获取今年1月1日0点0分0秒的时间戳
    getYearUnix:function(){
        var date = new Date();
        date.setMonth(0);
        date.setDate(1);
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date.getTime();
    },
    //获取标准年月日
    getLastDate:function(time){
        var date = new Date(time);
        var month = date.getMonth()+1<10?'0'+(date.getMonth()+1):date.getMonth()+1;
        var day = date.getDate()<10?'0'+date.getDate():date.getDate();
        return date.getFullYear()+'-'+month+'-'+day;
    },
    //转换时间
    getFormatTime:function(timestamp){
        var now = this.getUnix();
        var today = this.getTodayUnix();
        var year = this.getYearUnix();
        var timer =(now-timestamp)/1000;
        var tip = '';
        if(timer<=0){
            tip='刚刚';
        }else if(Math.floor(timer/60)<=0){
            tip='刚刚';
        }else if(timer<3600){
            tip = Math.floor(timer/60)+'分钟前';
        }else if(timer>=3600&&(timestamp-today>=0)){
            tip = Math.floor(timer/3600)+'小时前';
        }else if(timer/86400<=31){
            tip = Math.ceil(timer/86400)+'天前';
        }else{
            tip = this.getLastDate(timestamp);
        }
        return tip;
    }
};
export default{
    bind:function(el,binding){
        el.innerHTML = Time.getFormatTime(binding.value*1000);
        el.__timeout__ = setInterval(function(){
            el.innerHTML = Time.getFormatTime(binding.value*1000);
        },60000);
    },
    unbind:function(el){
        clearInterval(el.__timeout__);
        delete el.__timeout__;
    }
}
//components/daily-article.vue
<template>
    <div class="daily-article">
        <div class="daily-article-title">{{data.title}}</div>
        <div class="daily-article-content" v-html="data.body"></div>
        <div class="daily-comments" v-show="comments.length">
            <span>评论({{comments.length}})</span>
            <div class="daily-comment" v-for="comment in comments">
                <div class="daily-comment-avatar">
                    <img :src="comment.avator">
                </div>
                <div class="daily-comment-content">
                    <div class="daily-comment-name">{{comment.author}}</div>
                    <div class="daily-comment-time" v-time="comment.time"></div>
                    <div class="daily-comment-text">{{comment.content}}</div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    import Time from '../directives/time';
    import $ from '../libs/util';
    export default{
        directives:{Time},
        props:{
            id:{
                type:Number,
                default:0
            }
        },
        data(){
            return {
                data:{},
                comments:[]
            }
        },
        methods:{
            getArticle(){
                $.ajax.get('new/'+this.id).then(res=>{
                    //将文章中的图片地址替换为代理的地址
                    res.body = res.body.replace(/src="http/g,'src="'+$.imgPath+'http');
                    res.body = res.body.replace(/src="https/g,'src="'+$.imgPath+'https');
                    this.data = res;
                    //返回文章顶部
                    window.scrollTo(0,0);
                    this.getComments();
                })
            },
            getComments(){
                this.comments=[];
                $.ajax.get('story/'+this.id+'/short-comments').then(res=>{
                    this.comments = res.comments.map(comment=>{
                        //将头像的图片地址转为代理地址
                        comment.avatar = $.imgPath+comment.avatar;
                        return comment;
                    });
                })
            }
        },
        watch:{
            id(val){
                if(val)this.getArticle();
            }
        }
    };
</script>

    style.css

.daily-comments{
    margin:10px 0;
}
.daily-comments span{
    display: block;
    margin: 10px 0;
    font-size: 20px;
}
.daily-comment{
    overflow: hidden;
    margin-bottom: 20px;
    padding-bottom: 20px;
    border-bottom: 1px dashed #e3e8ee;
}
.daily-comment-avatar{
    width: 50px;
    height: 50px;
    float:left;
}
.daily-comment-avatar img{
    width: 100%;
    height: 100%;
    border-radius: 3px;
}
.daily-comment-content{
    margin-left: 65px;
}
.daily-comment-name{

}
.daily-comment-time{
    color: #9ea7b4;
    font-size: 14px;
    margin-top:5px;
}
.daily-comment-text{
    margin-top: 10px;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值