基于SpringBoot + Vue的个人博客系统10——标签和分类页面

简介

其实在主界面写好之后,分类和标签页面其实就很简单了,下面主要来介绍一下我踩过的坑。

实现

标签页面

1、因为主界面中的文章卡片可以复用,所以我们单独抽取成组件

组建封装

2、新建 @/components/ArticleItem.vue 组件

<template>
  <el-card>
    <div slot="header">
      <router-link class="main-text" :to="'/post/' + article.id" v-html="article.title"></router-link>
      <div class="article-info">
        <el-tag effect="dark" size="mini">原创</el-tag>
        浏览量:{{article.views}} 分类:
        <router-link
          class="link secondary-text"
          :to="'/category/'+article.category"
        >{{article.category}}</router-link>
      </div>
    </div>
    <div class="tabloid">{{article.tabloid}}</div>
    <i class="el-icon-user-solid article-icon">{{article.author}}</i>
    <i class="el-icon-date article-icon">{{article.gmtCreate}}</i>
    <i class="el-icon-price-tag article-icon">
      <router-link
        class="tag"
        v-for="(tag,index) in article.tags"
        :key="index"
        v-text="tag"
        :to="'/tag/'+tag"
      ></router-link>
    </i>
  </el-card>
</template>

<script>
export default {
  name: "ArticleItem",
  props: ["article"],
};
</script>

<style scoped>
.el-card {
  margin-top: 20px;
}
.article-info {
  margin-top: 10px;
  color: #909399;
  font-size: 13px;
}
.article-icon,
.article-icon .tag {
  color: #909399;
  font-size: 13px;
  margin-right: 10px;
  text-decoration: none;
}
.article-icon .tag:hover {
  color: #409eff;
  cursor: pointer;
}
.tabloid {
  color: #606266;
  font-size: 14px;
  margin-bottom: 10px;
}
</style>

代码都是从主页面抄过来的,就不在详述

3、在主界面引入组件

主页面引入组件

4、编写 Tag.vue 的 js 部分

import request from "@/http/request";

export default {
  name: "Tag",
  data() {
    return {
      pageInfo: [],
      tags: [],
    };
  },
  components: {
    ArticleItem: () => import("@/components/ArticleItem.vue"),
  },
  methods: {
    getArticlesInTagView(page, limit) {
      const tag = this.$route.params.name;
      if (tag === "all") {
        request
          .getArticles(page, limit)
          .then((res) => {
            if (res.code === 0) {
              this.pageInfo = res.data;
            } else {
              this.$notify.error({
                title: "提示",
                message: res.msg,
              });
            }
          })
          .catch((err) => {
            console.log(err);
            this.$notify.error({
              title: "提示",
              message: "网络忙,文章获取失败",
            });
          });
      } else {
        request
          .getArticlesByTag(tag, page, limit)
          .then((res) => {
            if (res.code === 0) {
              this.pageInfo = res.data;
            } else {
              this.$notify.error({
                title: "提示",
                message: res.msg,
              });
            }
          })
          .catch((err) => {
            console.log(err);
            this.$notify.error({
              title: "提示",
              message: "网络忙,文章获取失败",
            });
          });
      }
    },
    loadData() {
      request
        .getAllTags()
        .then((res) => {
          if (res.code === 0) {
            this.tags = res.data;
          } else {
            this.$notify.error({
              title: "提示",
              message: res.msg,
            });
          }
        })
        .catch((err) => {
          console.log(err);
          this.$notify.error({
            title: "提示",
            message: "网络忙,标签获取失败",
          });
        });
      this.getArticlesInTagView(1, 5);
    },
    handleCurrentChange(page) {
      this.getArticlesInTagView(page, 5);
    },
  },
  beforeRouteUpdate(to, from, next) {
    next();
    console.log(to);
    console.log(from);
    this.loadData();
  },
  created() {
    this.loadData();
  },
};

说明:

  • 组件创建的时候调用 loadData 函数从后端获取数据,如果路径参数为 all 的话就获取总的文章,否则根据分类获取文章
  • beforeRouteUpdate 函数在当前路由改变,但是该组件被复用时调用。举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。注意:这里写在 created 函数中不能实现对应效果,因为created 函数只调用了一次

加了 beforeRouteUpdate 函数之后我的项目报如下错误:

报错截图

百度了一下,在 @/router/index.js 中加入如下代码即可

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

上面网络请求对应 @/http/request.js 中的下面函数:

  getAllTags() {
    return instance.get(urls.tags).then(res => res.data);
  },
  getArticlesByTag(tag, page, limit) {
    return instance.get(urls.tag + "/" + tag, {
      params: {
        page: page,
        limit: limit
      }
    }).then(res => res.data);
  },

5、页面布局以及样式

<template>
  <el-row :gutter="0">
    <el-col :span="14" :offset="5">
      <div class="tag-box">
        <router-link :to="'/tag/' + tag" v-for="(tag,index) in tags" :key="index">
          <el-tag
            :class="tag === $route.params.name ? 'tag-selected' : ''"
            effect="plain"
            size="small"
          >{{tag}}</el-tag>
        </router-link>
      </div>
      <article-item v-for="article in pageInfo.records" :key="article.id" :article="article"></article-item>
      <el-pagination
        background
        @current-change="handleCurrentChange"
        :current-page.sync="pageInfo.current"
        :page-size="pageInfo.size"
        layout="prev, pager, next, jumper"
        :total="pageInfo.total"
        :hide-on-single-page="true"
      ></el-pagination>
    </el-col>
  </el-row>
</template>

<script>
// 此处省略......
</script>

<style scoped>
.tag-box {
  margin: 10px 0;
}
.tag-box .el-tag {
  margin: 0 2px;
}
.tag-box .el-tag:hover {
  background-color: #409eff;
  color: #ffffff;
  cursor: pointer;
}
.tag-selected {
  background-color: #409eff;
  color: #ffffff;
  cursor: pointer;
}
.el-pagination {
  margin: 20px 0;
  text-align: center;
}
</style>

说明:

  • 使用 el-col 实现分栏布局,总共24份,主体部分占14份,向右偏移5份
  • :class="tag === $route.params.name ? 'tag-selected' : ''" 实现被选中的标签改变样式的效果
  • 最下方分页效果和首页类似

6、页面效果

标签页面效果

分类页面

1、分类页面和标签页面基本类似,这里就直接上代码了

<template>
  <el-row :gutter="0">
    <el-col :span="14" :offset="5">
      <router-link
        :to="'/category/' + category.name"
        :class="category.name === $route.params.name ? 'category-item category-selected' : 'category-item'"
        v-for="(category,index) in categories"
        :key="index"
      >
        {{category.name}}
        <el-tag type="success" size="small" effect="dark">{{category.count}}</el-tag>
      </router-link>
      <article-item v-for="article in pageInfo.records" :key="article.id" :article="article"></article-item>
      <el-pagination
        background
        @current-change="handleCurrentChange"
        :current-page.sync="pageInfo.current"
        :page-size="pageInfo.size"
        layout="prev, pager, next, jumper"
        :total="pageInfo.total"
        :hide-on-single-page="true"
      ></el-pagination>
    </el-col>
  </el-row>
</template>

<script>
import request from "@/http/request";

export default {
  name: "Category",
  data() {
    return {
      pageInfo: [],
      categories: [],
    };
  },
  components: {
    ArticleItem: () => import("@/components/ArticleItem.vue"),
  },
  methods: {
    getArticlesInCategoryView(page, limit) {
      const category = this.$route.params.name;
      if (category === "all") {
        request
          .getArticles(page, limit)
          .then((res) => {
            if (res.code === 0) {
              this.pageInfo = res.data;
            } else {
              this.$notify.error({
                title: "提示",
                message: res.msg,
              });
            }
          })
          .catch((err) => {
            console.log(err);
            this.$notify.error({
              title: "提示",
              message: "网络忙,文章获取失败",
            });
          });
      } else {
        request
          .getArticlesByCategory(category, page, limit)
          .then((res) => {
            if (res.code === 0) {
              this.pageInfo = res.data;
            } else {
              this.$notify.error({
                title: "提示",
                message: res.msg,
              });
            }
          })
          .catch((err) => {
            console.log(err);
            this.$notify.error({
              title: "提示",
              message: "网络忙,文章获取失败",
            });
          });
      }
    },
    loadData() {
      request
        .getAllCategories()
        .then((res) => {
          if (res.code === 0) {
            this.categories = res.data;
          } else {
            this.$notify.error({
              title: "提示",
              message: res.msg,
            });
          }
        })
        .catch((err) => {
          console.log(err);
          this.$notify.error({
            title: "提示",
            message: "网络忙,分类获取失败",
          });
        });
      this.getArticlesInCategoryView(1, 5);
    },
    handleCurrentChange(page) {
      this.getArticlesInCategoryView(page, 5);
    },
  },
  beforeRouteUpdate(to, from, next) {
    next();
    console.log(to);
    console.log(from);
    this.loadData();
  },
  created() {
    this.loadData();
  },
};
</script>

<style scoped>
.category-item {
  display: inline-block;
  width: 20%;
  text-decoration: none;
  font-size: 14px;
  color: #606266;
  margin-top: 10px;
  margin-right: 10px;
  padding: 8px;
  transition: all 0.5s;
  border-radius: 5px;
  border: 1px solid #ebeef5;
}
.category-item:hover {
  box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 8px 0px;
  cursor: pointer;
  transform: scale(1.03);
  color: #303133;
}
.category-selected {
  box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 8px 0px;
  cursor: pointer;
  transform: scale(1.03);
  color: #303133;
}
.category-item .el-tag {
  float: right;
}
.el-pagination {
  margin: 20px 0;
  text-align: center;
}
</style>

2、页面效果

分类页面效果

参考代码:https://gitee.com/qianyucc/QBlog2/tree/v-7.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值