20. 主要页面的开发

本节编辑一些简单的页面,包括具体的文章分类页面、文章展示页面和主页展示页面等。

20.1. 1.主页

主页应当显示轮播图、导航菜单、位置列表和footer等内容,导航菜单、footer和文章列表属于可以被复用的内容,所以将其编写为组件。

首先设计代码结构,本项目所有的页面文件都放置在views文件夹中,所有的组件文件都放置在src/components文件夹中。

改造App.vue这个Vue.js的入口文件,将所有的路由页面都挂载在该页面中。

本项目的所有页面都包含顶部的导航和尾部的footer,将这两部分编写为组件。

在src/components文件夹中创建这两个组件,一个命名为NavView.vue,作为导航组件;

另一个命名为FooterView.vue,作为尾部组件。

导航组件获取服务器API中的NavMenu组件,该请求会在Created()生命周期发起,代码如下:

<script>
    export default {
        name: "NavView",
        data() {
            return {
                menu: [],
                index: 'MySite',
                theme1: 'light',
                userBtn: false,
                username: ''
            }
        },
        created() {
            //获取导航菜单
            this.$api.get('getNavMenu').then((res) => {
                //写数据
                this.menu = res.data
            })
        },
        updated() {},
    }
</script>

在页面中调用iView的Menu组件,使用v-for循环输出API获取数据,使用router-link组件设定超链接,代码如下:

<template>
    <div>
        <Menu mode="horizontal" :theme="theme1" active-name="1">
            <MenuItem name="1">
                <div class="main">
                    <Icon type="md-book"/>
                    <router-link to="/">
                        {{index}}
                    </router-link>
                </div>
            </MenuItem>
            <MenuItem v-for="item in menu" :name="item.name" :key="item.name">
                <router-link :to="item.src">
                    {{item.name}}
                </router-link>
            </MenuItem>
        </Menu>
    </div>
</template>
<style scoped>
    .main {
        font-weight: 600;
    }

    a {
        color: #2f2f2f;
    }
</style>

接着编写Footer.vue组件,和导航组件类似,获取API提供的数据并且循环输出,完整的代码如下:

<script>
    export default {
        name: "FooterView",
        data() {
            return {
                footer: [],
                icp:"Copyright © 2020 | 京ICP备0000"
            }
        },
        created() {
            //获取footer菜单
            this.$api.get('getFooter').then((res) => {
                this.footer = res.data
            })
        }
    }
</script>

这里没有使用动态的ICP备案信息,如果读者有兴趣,也可以从API中获取,接着编写页面。

获取的后台数据存在3个属性,其中,name表示显示,src表示连接,text表示显示在name后方的具体描述。

<template>
    <div class="footer">
        <div v-for="item in footer" :key="item.name" class="footer-div">
            {{item.name}}:
            <router-link :to="item.src">
                {{item.text}}
            </router-link>
        </div>
        <div class="icp">
            {{icp}}
        </div>
    </div>
</template>

<style scoped>
    .footer {
        background: #4a4b4a;
        color: azure;
        min-height: 200px;
        position: relative;
    }
    .footer a{
        color: aliceblue;
    }
    .footer-div{
        padding-top: 20px;
    }
    .icp{
        position: absolute;
        bottom: 10px;
        color: darkgrey;
        width: 100vw;
    }
</style>

编写好两个组件后,接着修改App.vue文件引入这两个组件,修改后的代码如下:

<template>
  <div id="app">
    <div class="nav">
      <NavView></NavView>
    </div>
    <router-view/>
    <div>
      <FooterView></FooterView>
    </div>
  </div>
</template>

<script>
import NavView from '@/components/NavView'
import FooterView from '@/components/FooterView'
export default {
  components: { FooterView, NavView}
}
</script>

这样所有的页面都可以显示这两个组件了,页面路径切换时这两个组件也不变,显示效果如图

../_images/image-20220719120254524.png

接着编写主页。在views文件夹下新建IndexView.vue文件用来编写主页面代码。页面路由文件router/index.js代码如下:

import { createRouter, createWebHashHistory } from 'vue-router'
import IndexView from "@/views/IndexView"; IndexView

const routes = [
  {
    path: '/',
    name: 'Index',
    component: IndexView
  },
......
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

主页面应当包含两个文章列表,文章列表本身也是一个可被复用的组件,所以提取为一个组件,在components文件夹中新建articleList.vue文件。

文章组件本身不需要通过接口获取任何数据,只需要显示上级组件中传递的数据。这里采用props方式传输数据,需要指定单击后跳转的链接,代码如下:

<template>
  <Card>
    <template #title>{{title}}</template>
<!--    <p slot="title">{{title}}</p>-->
    <p v-for="item in list" :key="item.id" class="item">
      <router-link :to="'/article/'+item.id">
        {{item.title}}
      </router-link>
    </p>
  </Card>
</template>

<script>
export default {
  name: 'ArticleList',
  data () {
    return {}
  },
  created () {

  },
  props: {
    title: String,
    list: Array
  }
}
</script>

<style scoped>
.item{
  border-bottom: 1px solid #eee;
  padding: 10px;
}
</style>

编写好该组件后,在IndexView.vue文件中引入。首页需要3个数据:热点文章、最新文章列表和轮播图。获取的列表数据中不显示后台没有上线的内容,代码如下:

<script>
import ArticleList from '../components/ArticleList'

export default {
  name: 'Home',
  components: {
    ArticleList
  },
  data() {
    return {
      value2: 0,
      pic: [],
      list: [],
      listTitle: '最新文章',
      hotList: [],
      hotListTitle: "最热文章"
    }
  },
  created: function () {
    //获取主页轮播图
    this.$api.get('getIndexPic').then((res) => {
      console.log(res.data)
      this.pic = res.data
    })
    //获取所有文章
    this.$api.get('getNewArticle').then((res) => {
      let rData = res.data.slice(0, 5)
      let tData = []
      rData.map((item) => {
        if (item.id !== 0) {
          tData.push(item)
        }
      })
      this.list = tData
    })
    //获得热点文章
    this.$api.get('getHotArticle').then((res) => {
      let rData = res.data.slice(0, 5)
      let tData = []
      rData.map((item) => {
        if (item.id !== 0) {
          tData.push(item)
        }
      })
      this.hotList = tData
    })
  }
}
</script>

接着编写相关的页面,主页使用iView中的走马灯插件完成轮播图,对两个应当出现的文章列表采用栅格式布局。完整的代码如下:

<template>
  <div>
    <!--轮播图-->
    <Carousel v-model="value2" loop>
      <CarouselItem :key="item.title" v-for="item in pic">
        <div class="demo-carousel">
          <router-link :to="item.src">
            <img :src="item.img"/>
            <div>
              {{item.title}}
            </div>
          </router-link>
        </div>
      </CarouselItem>
    </Carousel>
    <!--文章列表-->
    <div class="article-list">
      <row type="flex" justify="space-around" class="code-row-bg">
        <i-col span="11">
          <article-list :list="list" :title="listTitle"></article-list>
        </i-col>
        <i-col span="11">
          <article-list :list="hotList" :title="hotListTitle"></article-list>
        </i-col>
      </row>
    </div>
  </div>
</template>

<script>
import ArticleList from '../components/ArticleList'

export default {
  name: 'HomeView',
  components: {
    ArticleList
  },
  data () {
    return {
      value2: 0,
      pic: [],
      list: [],
      listTitle: '最新文章',
      hotList: [],
      hotListTitle: '最热文章'
    }
  },
  created: function () {
    // 获取主页轮播图
    this.$api.get('getIndexPic').then((res) => {
      console.log(res.data)
      this.pic = res.data
    })
    // 获取所有文章
    this.$api.get('getNewArticle').then((res) => {
      const rData = res.data.slice(0, 5)
      const tData = []
      rData.forEach((item) => {
        if (item.id !== 0) {
          tData.push(item)
        }
      })
      this.list = tData
    })
    // 获得热点文章
    this.$api.get('getHotArticle').then((res) => {
      const rData = res.data.slice(0, 5)
      const tData = []
      rData.forEach((item) => {
        if (item.id !== 0) {
          tData.push(item)
        }
      })
      this.hotList = tData
    })
  }
}
</script>
<style>
.demo-carousel {
  width: 98vw;
  height: 30vw;
}

.demo-carousel img {
  width: 100%;
  position: relative;
}

.demo-carousel div {
  padding: 30px;
  background: RGBA(0, 0, 0, 0.5);
  position: absolute;
  z-index: 1;
  color: white;
  font-size: 60px;
  width: 70%;
  top: 10vw;
  text-align: center;
  margin-left: 15%;
}
.article-list{
  padding: 20px 0 20px 0;
}
</style>