Django项目: 5.新闻主页

一、功能需求分析

1.功能

  1. 轮播图

  2. 推荐文章列表

  3. 文章标签导航

  4. 文章列表

  5. 分页

二、模型设计

根据功能分析,我们需要如下表

1.表和字段分析

  1. 文章分类表

  2. 文章表

  3. 文章评论表

  4. 推荐文章表

  5. 轮播图表

2.模型定义

定义一个基类模型,抽取公共字段

# 在utils目录下,创建一个models.py文件,在其中定义一个基类模型
from django.db import models


class BaseModel(models.Model):
    """
    基类,公共字段
    """
    create_time = models.DateTimeField('创建时间', auto_now_add=True)
    update_time = models.DateTimeField('更新时间', auto_now=True)
    is_delete = models.BooleanField('逻辑删除', default=False)

    class Meta:
        # 抽象类,用于继承,迁移时不会创建
        abstract = True

定义其他模型

# 在news目录下的models.py文件中定义如下数据模型
from django.db import models
from utils.models import BaseModel


class Tag(BaseModel):
    """
    文章分类标签模型
    """
    name = models.CharField('标签名', max_length=64, help_text='标签名')

    class Meta:
        ordering = ['-update_time', '-id']      # 排序
        db_table = "tb_tag"                     # 指明数据库表名
        verbose_name = "文章标签"                # 在admin站点中显示的名称
        verbose_name_plural = verbose_name      # 显示的复数名称

    def __str__(self):
        return self.name


class News(BaseModel):
    """
    文章模型
    """
    title = models.CharField('标题', max_length=150, help_text='标题')
    digest = models.CharField('摘要', max_length=200, help_text='摘要')
    content = models.TextField('内容', help_text='内容')
    clicks = models.IntegerField('点击量', default=0, help_text='点击量')
    image_url = models.URLField('图片url', default='', help_text='图片url')
    tag = models.ForeignKey('Tag', on_delete=models.SET_NULL, null=True)

    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)

    class Meta:
        ordering = ['-update_time', '-id']  # 排序
        db_table = "tb_news"  # 指明数据库表名
        verbose_name = "新闻"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return self.title


class Comments(BaseModel):
    """
    评论模型
    """
    content = models.TextField('内容', help_text='内容')
    author = models.ForeignKey('user.User', on_delete=models.SET_NULL, null=True)
    news = models.ForeignKey('News', on_delete=models.CASCADE)

    class Meta:
        ordering = ['-update_time', '-id']  # 排序
        db_table = "tb_comments"  # 指明数据库表名
        verbose_name = "评论"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<评论{}>'.format(self.id)


class HotNews(BaseModel):
    """
    推荐文章表
    """
    news = models.OneToOneField('News', on_delete=models.CASCADE)
    priority = models.IntegerField('优先级', help_text='优先级')

    class Meta:
        ordering = ['-update_time', '-id']  # 排序
        db_table = "tb_hotnews"  # 指明数据库表名
        verbose_name = "热门新闻"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<热门新闻{}>'.format(self.id)


class Banner(BaseModel):
    """
    轮播图
    """
    image_url = models.URLField('轮播图url', help_text='轮播图url')
    priority = models.IntegerField('优先级', help_text='优先级')

    news = models.OneToOneField('News', on_delete=models.CASCADE)

    class Meta:
        ordering = ['priority', '-update_time', '-id']  # 排序
        db_table = "tb_banner"  # 指明数据库表名
        verbose_name = "轮播图"  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        return '<轮播图{}>'.format(self.id)

三、文章标签导航功能

1.接口设计

  1. 接口说明:

    类目 说明
    请求方法 GET
    url定义 /
    参数格式 无参数
  2. 返回结果

    返回新闻页面,直接在模板渲染

2.后端代码

# 在news/views.py文件中定义如下视图
from django.shortcuts import render

from .models import Tag


def index(request):
    """
    新闻首页视图
    :param request:
    :return:
    """

    tags = Tag.objects.only('id', 'name').filter(is_delete=False)
    return render(request, 'news/index.html',
                  context={
                      'tags': tags
                  })

导入tag测试数据,或者直接用Navicat软件在tb_tag表中添加数据(数据见下方代码中引号里的内容,共6个),,,因为前段设置的原因,必须要跟我的内容一样,后面会用到

# insert news tag data

INSERT INTO tb_tag(name, create_time, update_time, is_delete) values
('Python基础', now(), now(), 0),
('Python高级', now(), now(), 0),
('Python函数', now(), now(), 0),
('PythonGUI', now(), now(), 0),
('Linux教程', now(), now(), 0),
('Python框架', now(), now(), 0);

3.前端代码

# 修改templates/news/index.html中 news-nav部分代码如:
        
          <nav class="news-nav">
              <ul class="clearfix">
                  <li class="active"><a href="javascript:void(0)">最新资讯a>li>
                    {% for tag in tags %}
                        <li><a href="javascript:void(0)" data-id="{{ tag.id }}">{{ tag.name }}a>
                  li>
                    {% endfor %}
              ul>
          nav>
        

四、新闻列表功能

1.业务流程分析

  1. 判断前端传递标签分类id是否为空,是否为整数,是否超过范围

  2. 判断前端传递当前文章页数是否为空,是否为整数,是否超过范围

2.接口设计

接口说明:

类目 说明
请求方法 GET
url定义 /news/
参数格式 查询参数

参数说明:

参数名 类型 是否必须 描述
tag 整数 标签分类id
page 整数 当前文章页数

返回结果:

{
    "errno": "0", 
     "errmsg": "", 
    "data": {
        "total_pages": 61,
        "news": [
            {
                'id': 'xxx',
                "digest": "在python用import或者from...import或者from...import...as...来导入相应的模块,作用和使用方法与C语言的include头文件类似。其实就是引入...",
                "title": "import方法引入模块详解",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python基础",
                "update_time": "2018年12月17日 14:48"
            },
            {
                'id': 'xxx'
                "digest": "如果你原来是一个php程序员,你对于php函数非常了解(PS:站长原来就是一个php程序员),但是现在由于工作或者其他原因要学习python,但是p...",
                "title": "给曾经是phper的程序员推荐个学习网站",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python基础",
                "update_time": "2018年12月17日 14:48"
            }
        ]
    }
}

3.后端代码

在项目根目录下创建一个media文件夹,用于存放新闻图片以及用户上传文件。

# 在settings.py文件中添加
# 媒体文件配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

django在调试模式下提供静态文件服务,为了能够返回media中的媒体文件还需在根urls.py中做如下配置

# 在根urls.py中加上static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('', include('news.urls')),
    path('', include('verification.urls')),
    path('user/', include('user.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

导入测试数据,为了测试数据的导入,请确保表名一致

# 在xshell中导入测试数据,在xshell中通过rz命令,将tb_news_20181217.sql文件上传到虚拟机

mysql -u 用户名 -p -D 数据库名 < tb_news_20181217.sql

 文件我已经放在我的文件栏里(名字是sql数据包.rar,解压后有4个文件,分别是标签表,新闻表,热门新闻表和轮播图表)

 

视图代码

import logging

from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator
from django.db.models import F

from .models import Tag, News
from . import constants
from utils.json_res import json_response

logger = logging.getLogger('django')

def index(request):
    """
    新闻首页视图
    url: /
    :param request:
    :return:
    """

    tags = Tag.objects.only('id', 'name').filter(is_delete=False)
    return render(request, 'news/index.html',
                  context={
                      'tags': tags
                  })


class NewsListView(View):
    """
    新闻列表视图
    url: /news/
    args: tag, page
    """
    def get(self, request):
        # 1.获取参数
        try:
            tag_id = int(request.GET.get('tag', 0))
        except Exception as e:
            logger.error('标签错误:\n{}'.format(e))
            tag_id = 0

        try:
            page = int(request.GET.get('page', 0))
        except Exception as e:
            logger.error('页码错误:\n{}'.format(e))
            page = 1
        # 使用only返回的是对象,所以传递到前端时需要迭代处理
        # news_queryset = News.objects.select_related('tag', 'author').only(
        #     'title', 'digest', 'image_url', 'update_time', 'tag__name', 'author__username')
        # 2.获取查询集 (values返回的是字典,only返回的是对象)
        ############## 重要 start #################
        # queryset:惰性 不会去数据库 
        # 构造,切片,过滤,传递的时候通常查询集不真正的去数据库查询
        # 什么时候,去数据库查呢:
            # 1.迭代
            # 2.切片,只要不是跳着去查就不会到数据库中查询,只有中间隔着数据切片的时候才会去数据库中查询,切出一个元素的时候
            # 3.照顾会在控制台打印的时候
            # 4.序列化缓存,把查询集的内容存到redis,内存
            # 5.使用len()方法获取长度,count
            # 6.使用list()方法转换类型
            # 7.bool,去判断是否为空的时候
        # QuerySet的缓存
        # 什么时候不缓存
            # 只执行查询集的一部分
            # 简单的打印不换车:Q = News.objects.all()        print(Q[1]) # 不会被缓存
            
           
        ############## 重要 end #################
        news_queryset = News.objects.values('id', 'title', 'digest', 'image_url', 'update_time').annotate(
            tag_name=F('tag__name'), author=F('author__username'))

        # 3.过滤
        # if tag_id:
        #     news = news_queryset.fileter(is_delete=False, tag_id=tag_id)
        # else:
        #     news = news_queryset.fileter(is_delete=False)
        news = news_queryset.filter(is_delete=False, tag_id=tag_id) or news_queryset.filter(is_delete=False)
        # 4.分页
        paginator = Paginator(news, constants.PER_PAGE_NEWS_COUNT) # 见下文讲解
        # 获取当前页数据 get_page 可以容错
        news_info = paginator.get_page(page)
        # 5.返回数据
        data = {
            'total_pages': paginator.num_pages, # 分了多少页
            'news':list(news_info)
        }
        return json_response(data=data)

paginator(分页器)方法:

Paginator.get_page(number)此方法为2.0新方法。

返回Page具有给定的从1开始的索引对象,同时还处理超出范围和无效页码,如果页码不是数字,则返回第一页。如果页码为负数或大于页数,则返回最后一页。

# 2.分页解析
        paginator = Paginator(docs, 5) #  每5份内容分页一次
        # 2.2 拿到前段发送过来的page这个查询参数的值
        page = paginator.get_page(request.GET.get('page',1))
        # request.GET.get('page',1) 获取url参数,127.0.0.1:8000/?page=
        # 非法数值则返回1 数值为空也返回1 如 127.0.0.1:8000/?page=asdsa
        # 获取当前(页码)所需要的文章列表 相当于一个容器

 

因为json默认不支持datetime类型数据,所以自定义json编码器(既让新闻的时间序列化)

# 在utils/json_res.py文件中添加自定义的json编码器以便能够序列化datetime数据类型
import json
import datetime
from django.http import JsonResponse

from .res_code import Code


# json编码器
# 自定义序列化器1,处理时间字段(老一点的版本这个就会报错)
#class MyJSONEncoder(json.JSONEncoder):
#    def default(self, o):
#        if isinstance(o, datetime.datetime):
#            return o.astimezone().strftime('%Y-%m-%d %H:%M:%S')  # 转换为本地时间
        
# 自定义序列化器2,处理时间字段(2.1.10django版本 DjangoJSONEncoder能够帮我们自动的实现)
class MyJSONEncoder(DjangoJSONEncoder):
    def default(self, o):
        if isinstance(o, datetime.datetime):
            return o.astimezone().strftime('%Y-%m-%d %H:%M:%S')  # 转换为本地时间

def json_response(errno=Code.OK, errmsg='', data=None, kwargs=None):
    json_dict = {
        'errno': errno,
        'errmsg': errmsg,
        'data': data
    }
    if kwargs and isinstance(kwargs, dict) :
        json_dict.update(kwargs)

    return JsonResponse(json_dict, encoder=MyJSONEncoder)

定义常量

# 在news目录下constants.py中定义如下常量:

# 每页新闻数
PER_PAGE_NEWS_COUNT = 5

路由配置

from django.urls import path
from . import views
# url的命名空间
app_name = 'news'

urlpatterns = [
    path('', views.index, name='index'),    # 将这条路由命名为index
    path('news/', views.NewsListView.as_view(), name='news_list')
]

4.前端代码

前端html页面代码修改


<div class="news-contain">
    <ul class="news-list">



    ul>
div>

js代码

// static/js/news/index.js
$(function () {
    // 新闻列表
    let $newNavLi = $('.news-nav ul li');   // 标签li
    let iPage = 1;                          // 默认第一页
    let iTotalPage = 1;                     // 默认总页数为1
    let iCurrentTagId = 0;                  // 默认分类标签为0
    let bIsLoadData = true;                 // 是否正在向后台加载数据
    
    fn_load_content();

    // 点击分类标签
    $newNavLi.click(function () {
        // 点击分类标签,则为点击的标签加上一个active的class属性
        // 并移除其他兄弟元素上的active的class属性
        $(this).addClass('active').siblings('li').removeClass('active');
        // 获取绑定在data-id属性上的tag_id
        let iClickTagId = $(this).children('a').attr('data-id');
        if (iClickTagId !== iCurrentTagId){
            iCurrentTagId = iClickTagId;  // 记录当前分类id
            // 重置分页参数
            iPage = 1;
            iTotalPage = 1;
            fn_load_content()
        }

    });

    // 页面滚动加载
    $(window).scroll(function () {
       // 浏览器窗口高度
        let showHeigtht = $(window).height();
       // 整个网页高度
        let pageHeight = $(document).height();
        //页面可以滚动的距离
        let canScrollHeight = pageHeight - showHeigtht;
        // 页面滚动了多少, 整个是随着页面滚动实时变化的
        let nowScroll = $(document).scrollTop();
        if ((canScrollHeight - nowScroll) < 100){
            if(!bIsLoadData){
                bIsLoadData = true;
                //判断页数,去更新新闻,小于总数才加载
                if(iPage < iTotalPage){
                    iPage += 1;
                    fn_load_content();

                }else {
                    message.showInfo('已全部加载,没有更多内容!');
                    $('a.btn-more').html('已全部加载,没有更多内容!')
                }

            }
        }
    });

    // 向后端获取新闻列表数据
    function fn_load_content() {
        $.ajax({
            url: '/news/',
            type: 'GET',
            data:{
                tag: iCurrentTagId,
                page: iPage
            },
            dataType: 'json',
            success: function (res) {
                if(res.errno === '0'){
                    iTotalPage = res.data.total_pages;
                    if(iPage === 1){
                        // 第一页清空内容
                        $('.news-list').html('')
                    }
                    res.data.news.forEach(function (one_news) {
                        let content = `                  
  • target="_blank"> ${one_news.title} title="${one_news.title}">

    <a href="#">${one_news.title}

    ${one_news.digest}

    ${one_news.tag_name} ${one_news.update_time} ${one_news.author}
  • `; $('.news-list').append(content); }); // $('.news-list').append($('滚动加载更多')); //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据 bIsLoadData = false; $('a.btn-more').html('滚动加载更多') }else { // 加载失败,打印错误信息 message.showError(res.errmsg) } }, error: function () { message.showError('服务器超时,请重试!') } }); } });

    五、轮播图功能

    1. 接口设计

    1. 接口说明:

    类目 说明
    请求方法 GET
    url定义 /news/banners/
    参数格式 无参数

    返回结果:

    {
        "errno": "0", 
         "errmsg": "OK", 
         "data": {
            "banners": [
                {
                    'image_url': '/media/jichujiaochen.jpeg',
                    'news_id': 221,
                    'news_title': "python 算法快速排序"
                },
                {
                    "image_url": "/media/python_advanced.jpg",
                    "news_id": 707,
                    "news_title": "Python 序列与映射的解包操作"
                }
            ]
        }
    }

    2.后端代码

    视图代码

    # 在news目录下views.py中创建如下视图
    class NewsBannerView(View):
        """
        轮播图视图
        url:/news/banners/
        """
        def get(self, request):
            banners = Banner.objects.values('image_url', 'news_id').annotate(
                news_title=F('news__title')
            ).filter(is_delete=False)[:constants.SHOW_BANNER_COUNT]
            data = {
                'banners': list(banners)
            }
            return json_response(data=data)

    定义常量

    # 在news目录下constants.py中定义如下常量
    # banner页展示数量
    SHOW_BANNER_COUNT = 6

    路由

    # news目录下urls.py中定义如下路由:
    from django.urls import path
    from . import views
    # url的命名空间
    app_name = 'news'
    
    urlpatterns = [
        path('', views.index, name='index'),    # 将这条路由命名为index
        path('news/', views.NewsListView.as_view(), name='news_list'),
        path('news/banners/', views.NewsBannerView.as_view(), name='news_banner')
    ]

    3.前端代码

    html代码

    
    
            <div class="banner">
                <ul class="pic">
                    
    
                ul>
                <a href="javascript:void(0);" class="btn prev">
                    <i class="PyWhich py-arrow-left">i>a>
                <a href="javascript:void(0);" class="btn next">
                    <i class="PyWhich py-arrow-right">i>a>
                <ul class="tab">
                    
    
                ul>
            div>
          

    js代码

    
        $(function () {
        // 新闻列表
        let $newNavLi = $('.news-nav ul li');   // 标签li
        let iPage = 1;                          // 默认第一页
        let iTotalPage = 1;                     // 默认总页数为1
        let iCurrentTagId = 0;                  // 默认分类标签为0
        let bIsLoadData = true;                 // 是否正在向后台加载数据
        
        fn_load_content();
    
        // 点击分类标签
        $newNavLi.click(function () {
            // 点击分类标签,则为点击的标签加上一个active的class属性
            // 并移除其他兄弟元素上的active的class属性
            $(this).addClass('active').siblings('li').removeClass('active');
            // 获取绑定在data-id属性上的tag_id
            let iClickTagId = $(this).children('a').attr('data-id');
            if (iClickTagId !== iCurrentTagId){
                iCurrentTagId = iClickTagId;  // 记录当前分类id
                // 重置分页参数
                iPage = 1;
                iTotalPage = 1;
                fn_load_content()
            }
    
        });
    
        // 页面滚动加载
        $(window).scroll(function () {
           // 浏览器窗口高度
            let showHeigtht = $(window).height();
           // 整个网页高度
            let pageHeight = $(document).height();
            //页面可以滚动的距离
            let canScrollHeight = pageHeight - showHeigtht;
            // 页面滚动了多少, 整个是随着页面滚动实时变化的
            let nowScroll = $(document).scrollTop();
            if ((canScrollHeight - nowScroll) < 100){
                if(!bIsLoadData){
                    bIsLoadData = true;
                    //判断页数,去更新新闻,小于总数才加载
                    if(iPage < iTotalPage){
                        iPage += 1;
                        fn_load_content();
    
                    }else {
                        message.showInfo('已全部加载,没有更多内容!');
                        $('a.btn-more').html('已全部加载,没有更多内容!')
                    }
    
                }
            }
        });
    
        // 向后端获取新闻列表数据
        function fn_load_content() {
            $.ajax({
                url: '/news/',
                type: 'GET',
                data:{
                    tag: iCurrentTagId,
                    page: iPage
                },
                dataType: 'json',
                success: function (res) {
                    if(res.errno === '0'){
                        iTotalPage = res.data.total_pages;
                        if(iPage === 1){
                            // 第一页清空内容
                            $('.news-list').html('')
                        }
                        res.data.news.forEach(function (one_news) {
                            let content = `                  
  • target="_blank"> ${one_news.title} title="${one_news.title}">

    <a href="#">${one_news.title}

    ${one_news.digest}

    ${one_news.tag_name} ${one_news.update_time} ${one_news.author}
  • `; $('.news-list').append(content); }); // $('.news-list').append($('滚动加载更多')); //数据加载完毕,设置正在加载数据变量为false,表示当前没有加载数据 bIsLoadData = false; $('a.btn-more').html('滚动加载更多') }else { // 加载失败,打印错误信息 message.showError(res.errmsg) } }, error: function () { message.showError('服务器超时,请重试!') } }) } // 新闻轮播图功能 // 1.加载轮播图数据 function fn_load_banner() // 2.点击导航按钮切换 用this的话必须写function()了不能用()=> // 3.上一页,下一页 // 4.自动切换 // 5.鼠标滑入暂停自动播放 fn_load_banner(); // 先加载banner let $banner = $('.banner'); // banner容器div let $picLi = $('.banner .pic li'); // 图片li标签 let $pre = $('.banner .prev'); // 上一张 let $next = $('.banner .next'); // 下一张 let $tabLi = $('.banner .tab li'); // 按钮 let index = 0; // 当前索引 // 导航小圆点 $tabLi.click(function () { index = $(this).index(); $(this).addClass('active').siblings('li').removeClass('active'); $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500); }); // 点击切换上一张 $pre.click(()=> { index --; if(index<0){ index = $tabLi.length - 1 // 最后一张 } $tabLi.eq(index).addClass('active').siblings('li').removeClass('active'); $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500); }); // 点击切换下一张 $next.click(()=>{ auto(); }); // 图片向前滑动 function auto() { index ++; index %= $tabLi.length; $tabLi.eq(index).addClass('active').siblings('li').removeClass('active'); $picLi.eq(index).fadeIn(1500).siblings('li').fadeOut(1500) } // 定时器 let timer = setInterval(auto, 2500); $banner.hover( ()=>{ clearInterval(timer) }, ()=>{ timer = setInterval(auto, 2500); } ); // 定义向后端获取banner的ajax数据 function fn_load_banner() { $ .ajax({ url: '/news/banners/', type: 'GET', async: false, // 同步执行,下面的代码依赖banner的加载 dataType: "json", }) .done( (res)=> { if(res.errno === '0'){ let content = ''; let tab_content = ''; res.data.banners.forEach( (one_banner, index) =>{ if(index === 0){ // 第一页 加active属性 content = `
  • ${one_banner.news_title}
  • `; tab_content = '
  • '; }else { content = `
  • ${one_banner.news_title}
  • `; tab_content = '
  • '; } $('.pic').append(content); $('.tab').append(tab_content) }) }else { message.showError(res.errmsg) } }) .fail(()=>{ message.showError('服务器超时,请重试!') }) } });

    六、推荐新闻

    1. 接口设计

    1. 接口说明:

    类目 说明
    请求方法 GET
    url定义 /
    参数格式 无参数

    2.返回内容

    返回新闻页面,直接在模板渲染

    2.后端代码

    视图代码

    # 修改news/views.py中的index视图
    def index(request):
        """
        新闻首页视图
        url: /
        :param request:
        :return:
        """
    
        tags = Tag.objects.only('id', 'name').filter(is_delete=False)
        
        hot_news = HotNews.objects.select_related('news').only('news__title', 'news__image_url', 'news_id').filter(
            is_delete=False
        ).order_by('priority', '-news__clicks')[:constants.SHOW_HOTNEWS_COUNT]
        return render(request, 'news/index.html',
                      context={
                          'tags': tags,
                          'hot_news': hot_news
                      })

    定义常量

    # 在news/constants.py中定义下面的常量
    # 显示热门新闻条数
    SHOW_HOTNEWS_COUNT = 3

    3.前端代码

    html代码

    
    <ul class="recommend-news">
        {% for item in hot_news %}
        <li>
            <a href="https://www.shiguangkey.com/course/2432" target="_blank">
                <div class="recommend-thumbnail">
                    <img src="{{ item.news.image_url }}" alt="title">
                div>
                <p class="info">{{ item.news.title }}p>
            a>
        li>
        {% endfor %}
    ul>
    

     

    你可能感兴趣的:(Django项目: 5.新闻主页)