Spring Boot + vue-element 开发个人博客项目实战教程(三十、文章的发布状态)

112 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

首先在article.js文件中修改一下根据id获取文章的接口地址。

export function getArticleById(id){
  return request({
    url: '/article/getArticle/' + id,
    method: 'get'
  })
}

还有之前我们点击发布文章之后,没有返回到列表页,现在我们先添加上这个功能,只要在之前写的方法里添加上跳转的地址即可。添加这一句话:this.$router.push("/articles/list");

 var body = this.article;
      addArticle(body).then(res => {
        if(res.code === 200) {
          this.$notify({
              title: "文章发表成功",
              message: `文章《${this.article.title}》发表成功!`,
              type: "success",
          });
          this.$router.push("/articles/list");
        } else {
          this.$notify({
              title: "文章发表失败",
              message: `文章《${this.article.title}》发表失败!`,
              type: "error",
          });
        }
         this.showDialog = false;
     })

同时在发布草稿的方法里也要加上这一句。

  // ------- 保存草稿
    saveDraft() {
      this.article.artStatus = 3;
      if (this.article.title.trim() == "") {
        this.$message.error("文章标题不能为空");
        return false;
      }
      if (this.article.content.trim() == "") {
        this.$message.error("文章内容不能为空");
        return false;
      }
      var body = this.article;
      addArticle(body).then(res => {
        if(res.code === 200) {
          this.$message({
            type: 'success',
            message: '保存草稿成功!'
          });
          this.$router.push("/articles/list");
        } else {
          this.$message({
            type: 'error',
            message: '保存草稿失败!'
          });
        }
      })
    },

列表页面

页面和其他的列表差不多,这里我按照模块展示。

我先将return方法里的参数写出来。

data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10,
        categoryId: null,
        artStatus: null,
        title: null
      },
      categoryId: null,
      categoryList: [],
      tagId: null,
      tagList: [],
      title: null,
      typeList: [
        {
          value: 1,
          label: "发布"
        },
        {
          value: 2,
          label: "仅我可见"
        },
        {
          value: 3,
          label: "草稿"
        }
      ],
      artStatus: null,
      views: null,
      totalWords: null,
      description: null
    }
  },

然后将接口导入进来

import { articleList, deleteArticle } from '@/api/article'
import { getCategory } from '@/api/category'
import { getTag } from '@/api/tag'

接着写页面功能。

    <!-- 设置标题文章管理 -->
    <div slot="header" class="clearfix">
      <span>文章列表</span>
    </div>

分类查询

废话不多说直接上代码,完整代码再最后,可直接跳过看完整代码。

     <!-- 文章分类 -->
        <el-select
          clearable
          size="small"
          v-model="categoryId"
          filterable
          placeholder="请选择分类"
          style="margin-right:1rem"
        >
          <el-option
            v-for="item in categoryList"
            :key="item.id"
            :label="item.categoryName"
            :value="item.categoryId"
          />
        </el-select>

这里遍历了categoryList,而这个list则是查询全部分类获取的,所以我们要写一个方法来查询分类,还要在页面初始的时候就要查询出来。接口还是那个添加文章的分类下拉的。

getCategoriesList() {
      var categoryName = "";
      getCategory({categoryName}).then(response => {
         this.categoryList = response.data;
      })
},

现在是有值了,然后再初始化页面的时候就加载完成。

  created() {
    this.getList();
    this.getCategoriesList();
  },

好啦,这时候可以看一下页面:

4.2.2、文章类型查询

接下来我们再继续写文章的类型查询。

<!-- 文章类型 -->
  <el-select
    clearable
    v-model="artStatus"
    placeholder="请选择文章类型"
    size="small"
    style="margin-right:1rem"
    >
    <el-option
      v-for="item in typeList"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>

typeList这个数据就是data方法中的数组,在上边已经列出来了

typeList: [
        {
          value: 1,
          label: "发布"
        },
        {
          value: 2,
          label: "仅我可见"
        },
        {
          value: 3,
          label: "草稿"
        }
],

同样也要在页面加载的时候一起加载数据,将方法也要放到created中

 created() {
    this.getList();
    this.getCategoriesList();
    this.getTagsList();
  },

文章标题查询

还有一个标题的搜索和点击搜索的按钮,当点击搜索的按钮触发查询的接口。

    	<!-- 文章名 -->
        <el-input
          clearable
          v-model="title"
          prefix-icon="el-icon-search"
          size="small"
          placeholder="请输入文章名"
          style="width:200px"
          @keyup.enter.native="searchArticles"
        />

        <el-button
          type="primary"
          size="small"
          icon="el-icon-search"
          style="margin-left:1rem"
          @click="searchArticles"
        >
          搜索
        </el-button>

这里用到了一个方法:searchArticles

searchArticles() {
  this.getList();
},

这里面又调用了getList方法

getList() {
      this.listLoading = true
      this.listQuery.categoryId = this.categoryId;
      this.listQuery.title = this.title;
      this.listQuery.artStatus = this.artStatus;

      var body = this.listQuery;
      articleList({body}).then(response => {
        this.list = response.data.result
        this.count = response.data.totalSize
        this.listLoading = false
      })
  },

下面可以写数据展示的表格了,这个没什么好说的,直接上代码

<el-table v-loading="listLoading" :data="list" fit highlight-current-row style="width: 98%; margin-top:30px;">
      <el-table-column align="center" label="ID" >
        <template slot-scope="scope">
          <span>{{ scope.row.id }}</span>
        </template>
      </el-table-column>

      <el-table-column label="文章封面" width="180" align="center">
        <template slot-scope="scope">
          <img
            class="article-cover"
            :src=" scope.row.imageUrl" />
        </template>
      </el-table-column>

      <!-- 文章标题 -->
      <el-table-column prop="title" label="标题" align="center" />

      <!-- 文章分类 -->
      <el-table-column prop="categoryName" label="分类" width="110" align="center"/>

      <!-- 文章标签 -->
      <el-table-column prop="tagList" label="标签" width="170" align="center">
        <template slot-scope="scope">
          <el-tag
            v-for="item of scope.row.tagList"
            :key="item.id"
            style="margin-right:0.2rem;margin-top:0.2rem"
          >
            {{ item.tagName }}
          </el-tag>
        </template>
      </el-table-column>

      <!-- 文章浏览量 -->
      <el-table-column
        prop="views"
        label="浏览量"
        width="70"
        align="center"
      >
        <template slot-scope="scope">
          <span v-if="scope.row.views">
            {{ scope.row.views }}
          </span>
          <span v-else>0</span>
        </template>
      </el-table-column>
      <!-- 文章总字数 -->
      <el-table-column
        prop="totalWords"
        label="总字数"
        width="70"
        align="center"
      >
        <template slot-scope="scope">
          <span v-if="scope.row.totalWords">
            {{ scope.row.totalWords }}
          </span>
          <span v-else>0</span>
        </template>
      </el-table-column>

      <!-- 文章描述 -->
      <el-table-column prop="description" label="描述" align="center" />

      <el-table-column align="center" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" icon="el-icon-edit" @click="editArticle(scope.row.id)">编辑</el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteArticleById(scope.row.id)" >删除</el-button>
        </template>
      </el-table-column>

    </el-table>

    <!-- 分页 -->
    <el-pagination
      class="pagination-container"
      background
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="this.listQuery.pageNum"
      :page-size="this.listQuery.pageSize"
      :total="count"
      :page-sizes="[10, 20, 30]"
      layout="total, sizes, prev, pager, next, jumper"
    />

方法:

 handleSizeChange(pageSize) {
      this.listQuery.pageSize = pageSize
      this.getList()
    },
 handleCurrentChange(pageNum) {
      this.listQuery.pageNum = pageNum
      this.getList()
  },

以上就是展示的功能,下面我们来写删除和编辑的,这个就比较简单了,和之前的基本上一样。

5、删除

删除后端的接口传的参数格式做了修改,我记得没有写。这里我先写出来,大家如果没有更改就修改一下,如果修改了就过掉。

	/**
     * 删除文章
     * @return
     */
    @ApiOperation(value = "删除文章")
    @DeleteMapping("/delete")
    @OperationLogSys(desc = "删除文章", operationType = OperationType.DELETE)
    public JsonResult<Object> articleDelete(@RequestParam(value = "id") int id) {
        articleService.deleteArticle(id);
        return JsonResult.success();
    }

前端接口:

export function deleteArticle(id) {
  return request({
    url: '/article/delete',
    method: 'delete',
    params: { id }
  })
}

删除的方法:

 deleteArticleById (id) {
      this.$confirm('此操作将永久删除该文章, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteArticle(id).then(response => {
           this.$message({
            type: 'success',
            message: '删除成功!'
          })
           this.getList()
        }).catch(() => {
          console.log('error')
        })
      }).catch(() => {
         this.$message({
            type: 'error',
            message: '你已经取消删除该文章!'
          })
      })
    },

这里没啥好说的,和之前的删除操作基本上一样。

6、修改功能

修改功能稍微做了一点的改变,我们看一下表格的操作栏的编辑按钮

<el-button type="primary" size="mini" icon="el-icon-edit" @click="editArticle(scope.row.id)">编辑</el-button>

点击时间绑定了一个方法,我们要传入文章的id

 editArticle(id) {
      this.$router.push({ name: 'Addrticles', params: { id: id }});
  },

这个和之前的公告差不多,只是将这个跳转提取到了方法内实现。

相对应的,在add页面中进行接收。

  created() {
    const id = this.$route.params.id;
    if(id) {
      getArticleById(id).then((res) => {
        
        console.log(res.data)
        this.article = res.data;
      });
    }
  },

这是编辑的功能也修改好了。

看着还可以,大家可以自己美化一下页面,到这里文章的所有功能基本上全部完成了。

以下是列表的全部代码:

<template>
    <el-card class="box-card">

    <!-- 设置标题文章管理 -->
    <div slot="header" class="clearfix">
      <span>文章列表</span>
    </div>

    <!-- 文章按条件查找 -->
     <div style="margin-left:auto">
       <!-- 文章分类 -->
        <el-select
          clearable
          size="small"
          v-model="categoryId"
          filterable
          placeholder="请选择分类"
          style="margin-right:1rem"
        >
          <el-option
            v-for="item in categoryList"
            :key="item.id"
            :label="item.categoryName"
            :value="item.categoryId"
          />
        </el-select>
        <!-- 文章类型 -->
        <el-select
          clearable
          v-model="artStatus"
          placeholder="请选择文章类型"
          size="small"
          style="margin-right:1rem"
        >
          <el-option
            v-for="item in typeList"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
        <!-- 文章名 -->
        <el-input
          clearable
          v-model="title"
          prefix-icon="el-icon-search"
          size="small"
          placeholder="请输入文章名"
          style="width:200px"
          @keyup.enter.native="searchArticles"
        />

        <el-button
          type="primary"
          size="small"
          icon="el-icon-search"
          style="margin-left:1rem"
          @click="searchArticles"
        >
          搜索
        </el-button>
     </div>

    <el-table v-loading="listLoading" :data="list" fit highlight-current-row style="width: 98%; margin-top:30px;">
      <el-table-column align="center" label="ID" >
        <template slot-scope="scope">
          <span>{{ scope.row.id }}</span>
        </template>
      </el-table-column>

      <el-table-column label="文章封面" width="180" align="center">
        <template slot-scope="scope">
          <img
            class="article-cover"
            :src=" scope.row.imageUrl" />
        </template>
      </el-table-column>

      <!-- 文章标题 -->
      <el-table-column prop="title" label="标题" align="center" />

      <!-- 文章分类 -->
      <el-table-column prop="categoryName" label="分类" width="110" align="center"/>

      <!-- 文章标签 -->
      <el-table-column prop="tagList" label="标签" width="170" align="center">
        <template slot-scope="scope">
          <el-tag
            v-for="item of scope.row.tagList"
            :key="item.id"
            style="margin-right:0.2rem;margin-top:0.2rem"
          >
            {{ item.tagName }}
          </el-tag>
        </template>
      </el-table-column>

      <!-- 文章浏览量 -->
      <el-table-column
        prop="views"
        label="浏览量"
        width="70"
        align="center"
      >
        <template slot-scope="scope">
          <span v-if="scope.row.views">
            {{ scope.row.views }}
          </span>
          <span v-else>0</span>
        </template>
      </el-table-column>
      <!-- 文章总字数 -->
      <el-table-column
        prop="totalWords"
        label="总字数"
        width="70"
        align="center"
      >
        <template slot-scope="scope">
          <span v-if="scope.row.totalWords">
            {{ scope.row.totalWords }}
          </span>
          <span v-else>0</span>
        </template>
      </el-table-column>

      <!-- 文章描述 -->
      <el-table-column prop="description" label="描述" align="center" />

      <el-table-column align="center" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" icon="el-icon-edit" @click="editArticle(scope.row.id)">编辑</el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteArticleById(scope.row.id)" >删除</el-button>
        </template>
      </el-table-column>

    </el-table>

    <!-- 分页 -->
    <el-pagination
      class="pagination-container"
      background
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="this.listQuery.pageNum"
      :page-size="this.listQuery.pageSize"
      :total="count"
      :page-sizes="[10, 20, 30]"
      layout="total, sizes, prev, pager, next, jumper"
    />

    </el-card>
</template>

<script>

import { articleList, deleteArticle } from '@/api/article'
import { getCategory } from '@/api/category'
import { getTag } from '@/api/tag'

export default {
  name: 'articleList',

  created() {
    this.getList();
    this.getCategoriesList();
    this.getTagsList();
  },

  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10,
        categoryId: null,
        artStatus: null,
        title: null
      },
      categoryId: null,
      categoryList: [],
      tagId: null,
      tagList: [],
      title: null,
      typeList: [
        {
          value: 1,
          label: "发布"
        },
        {
          value: 2,
          label: "仅我可见"
        },
        {
          value: 3,
          label: "草稿"
        }
      ],
      artStatus: null,
      views: null,
      totalWords: null,
      description: null
    }
  },

  methods: {

    getList() {
      this.listLoading = true
      this.listQuery.categoryId = this.categoryId;
      this.listQuery.title = this.title;
      this.listQuery.artStatus = this.artStatus;

      var body = this.listQuery;
      articleList({body}).then(response => {
        this.list = response.data.result
        this.count = response.data.totalSize
        this.listLoading = false
      })
    },

    editArticle(id) {
      this.$router.push({ name: 'Addrticles', params: { id: id }});
    },

    getCategoriesList() {
      var categoryName = "";
      getCategory({categoryName}).then(response => {
         this.categoryList = response.data;
      })
    },

    getTagsList() {
      var tagName = "";
      getTag({tagName}).then(response => {
         this.tagList = response.data;
      })
    },

    searchArticles() {
      this.getList();
    },

    handleSizeChange(pageSize) {
      this.listQuery.pageSize = pageSize
      this.getList()
    },
    handleCurrentChange(pageNum) {
      this.listQuery.pageNum = pageNum
      this.getList()
    },


    deleteArticleById (id) {
      this.$confirm('此操作将永久删除该文章, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteArticle(id).then(response => {
           this.$message({
            type: 'success',
            message: '删除成功!'
          })
           this.getList()
        }).catch(() => {
          console.log('error')
        })
      }).catch(() => {
         this.$message({
            type: 'error',
            message: '你已经取消删除该文章!'
          })
      })
    },

  },

 
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .pagination-container {
    float: right;
    margin-top: 1.25rem;
    margin-bottom: 1.25rem;
  }
  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
  .article-cover {
    position: relative;
    width: 100%;
    height: 90px;
    border-radius: 4px;
  }
  .article-cover::after {
    content: "";
    background: rgba(0, 0, 0, 0.3);
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
  }

</style>