Spring Boot + vue-element 开发个人博客项目实战教程(二十一、个人介绍、公告管理、标签管理开发)

1,163 阅读8分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

今天引用个名剧来作为开头,希望大家要努力的去学习,一定要将自己的基础巩固好。

在这个舞台上,人们看不到生活的出路,终日无所事事,极端痛苦,又极端无聊,渴望改变,却又无力自拔。大家都在盼望着有个什么人或者什么事情突然出现,生活从此彻底改变。每个人都等待着,盼望着,至于;到底在等什么,却没有人能说清。就像弗拉季米尔说的那样:“在这场大混乱里,只有一样东西是清楚的。咱们在等待戈多的到来。《等待戈多》

一、个人介绍

个人介绍部分主要是写写我们自己的个人简介等信息,可以写一下我们学到的一些技术等东西,相当于我们的个人简历。

这个我们不做太多的操作,只展示下信息即可,或者我们的简历放上去,因为后台没有写这个接口,或者后期优化,我们暂时先将数据写死,先来画页面。

打开/views/user下的introduction.vue文件 下面是我简单的写了一下,大家可以根据自己的需求进行补充!

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

 
    <el-card class="box-card" >
      <div slot="header" class="clearfix">
        <span>个人简介</span>
      </div>
      <div class="text item">
        本人是一个学习Java的小菜鸟,会一些前端和后端。。。。。。
      </div>
    </el-card>
 
 </el-card>
</template>

<script>
export default {

}
</script>


<style>

  .text {
    font-size: 14px;
  }

  .item {
    margin-bottom: 18px;
  }

  .box-card {
    width: 70%;
    float: left;
    margin: 2% 6%;
  }
  .box-card1 {
    width: 98%;
    height: 90vh;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }

</style>

二、公告管理

下面我们开始写公告管理,这个我们之前已经将公告文件和路由写好了,我们还需要去写api接口和页面即可。还是和之前一样,我们打开/src/api文件夹,然后新建一个notice.js文件,用来写和后端对接的接口。

我们先改造一下公告的后端代码,我们之前的分页查询进行了改动,现在公告的列表也是分页查询,所以我们要改一下。

1、公告列表

@ApiOperation(value = "公告列表")
@PostMapping("/list")
public JsonResult<Object> listPage(@RequestBody @Valid PageRequestApi<PageRequest> pageRequest) {
    List<Notice> noticeList = noticeService.getNoticePage(pageRequest.getBody());
    PageInfo pageInfo = new PageInfo(noticeList);
    PageResult pageResult = PageUtil.getPageResult(pageRequest.getBody(), pageInfo);
    return JsonResult.success(pageResult);
 }

然后打开刚才新建的notice.js,写一个查询的接口

import request from '@/utils/request'

export function noticeList(query) {
    return request({
      url: '/notice/list',
      method: 'post',
      data: query
    })
}

接下来打开/views/notice下面的list.vue文件,还是和我们之前写日志的一样,我们先将查询的基础写好。

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

    <!-- 设置标题公告管理 -->
    <div slot="header" class="clearfix">
      <span>公告列表</span>
    </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.noticeId }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="公告标题" >
        <template slot-scope="scope">
          <span>{{ scope.row.noticeTitle }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="公告类型" >
        <template slot-scope="scope">
          <span>{{ scope.row.noticeType }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="状态" >
        <template slot-scope="scope">
          <span>{{ scope.row.status }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="创建者" >
        <template slot-scope="scope">
          <span>{{ scope.row.createBy }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="创建时间" >
        <template slot-scope="scope">
          <span>{{ scope.row.createTime }}</span>
        </template>
      </el-table-column>

       <el-table-column align="center" label="更新时间" >
        <template slot-scope="scope">
          <span>{{ scope.row.updateTime }}</span>
        </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 { noticeList } from '@/api/notice'

export default {
  name: 'noticeList',

  created() {
    this.getList()
  },

  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      }
    }
  },

  methods: {

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

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

  }
 
}
</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;
  }
</style>

写完之后,然后我们去查看页面

在这里插入图片描述 看到页面上有数据了,此时我们会发现公告类型和状态都是数字,并不是我们要的文字,我们打开后端代码,查看实体类对应的字段。

在这里插入图片描述 这个是我们当初设计数字代表的状态,所以,现在我们就去页面将数字转换成文字状态。我们写一个方法,然后将这些数字转换成文字。

<el-table-column align="center" label="公告类型" >
     <template slot-scope="scope">
        <span>{{filterStatus( scope.row.noticeTitle )}}</span>
     </template>
</el-table-column>

<el-table-column align="center" label="状态" >
     <template slot-scope="scope">
       <span>{{ noticeStatus(scope.row.status) }}</span>
     </template>
</el-table-column>
 
filterStatus(val) {
    if (val == "0") {
       return "公告";
    } else if (val == "1") {
       return "通知";
    } else {
       return "提醒";
    }
 },
 noticeStatus(val) {
    if (val == "0") {
      return "正常";
    } else {
      return "关闭";
    }
 }

在这里插入图片描述 看到还有时间不对,还是和之前一样,我们去后台修改。打开Notice.java

    /**
     * 创建时间
     */
    @JsonFormat(timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @JsonFormat(timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

修改完成后,重启后端项目,然后刷新页面就正常显示了。

接下来我们还要增加一个操作列,写入编辑和删除公告。 我们先写删除功能,这个比较简单。

2、公告删除

<el-table-column align="center" label="操作" width="180">
    <template slot-scope="scope">
       <el-button type="primary" size="mini" icon="el-icon-edit">编辑</el-button>
       <el-button type="danger" size="small" icon="el-icon-delete" >删除</el-button>
    </template>
</el-table-column>

修改一下后端的传参方式

    /**
     * 删除
     * @return
     */
    @ApiOperation(value = "删除公告")
    @PostMapping("/delete")
    @OperationLogSys(desc = "删除公告", operationType = OperationType.DELETE)
    public JsonResult<Object> noticeDelete(@RequestParam(value = "id") int id) {
        noticeService.deleteNotice(id);
        return JsonResult.success();
    }

在api的文件中将notice.js中新添加一个删除的接口

export function deleteNotice(id) {
  return request({
    url: '/notice/delete',
    method: 'post',
    params: { id }
  })
}

然后在notice.vue中添加删除的方法

import { noticeList, deleteNotice } from '@/api/notice'
 deleteNoticeById (id) {
      this.$confirm('此操作将永久删除该公告, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteNotice(id).then(response => {
           this.$message({
            type: 'success',
            message: '删除成功!'
          })
           this.getList()
        }).catch(() => {
          console.log('error')
        })
      }).catch(() => {
         this.$message({
            type: 'error',
            message: '你已经取消删除该公告!'
          })
      })
    },

我们先写发布公告的功能,因为我们发布公告是单独的一页,所以我们打开/views/notice/add.vue先将基础的页面也好。

3、发布公告

<template>
    <el-card class="box-card">
    <!-- 设置标题公告管理 -->
    <div slot="header" class="clearfix">
      <span>添加公告</span>
    </div>



     </el-card>
</template>

<script>


export default {
  name: 'Addnotice',

   data() {
    return {
      
    }
  },

  methods: {

  }

 
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
</style>

然后我们再去api中将添加和修改的两个接口写完整。

export function addNotice(data) {
  return request({
    url: '/notice/create',
    method: 'post',
    data
  })
}

export function updateNotice(data) {
  return request({
    url: '/notice/update',
    method: 'post',
    data
  })
}

下面我们去画页面。我们这里使用了一个新的知识点,使用markdown编辑公告内容,所以这里我们引用一个富文本插件mavon-editor

(1)安装mavon-editor

npm install mavon-editor --save

(2)在main.js中全局注册该组件

import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
// use mavonEditor
Vue.use(mavonEditor)

在这里插入图片描述 然后我们就可以在页面上直接使用了。

打开发布公告的页面,我们引入该组件。

 <!-- 公告内容 -->
    <mavon-editor ref="md" v-model="notice.noticeContent" style="height:calc(100vh - 260px)"/>
 data() {
    return {
      notice: {
        noticeContent: ""
      }
    }
  },

添加完之后,我们运行项目,打开页面看一下,神奇的时刻到来了。

在这里插入图片描述

这时就出现了一个编辑框,我们在左边输入右边会显示效果,这个是使用markdown来编写文章,如果有不会的小伙伴,百度搜一下markdown用法就会有很多,没有多少东西,大家作为程序员要掌握的一个小技能。

接下来我们完成公告的数据提交功能,所以我们接下来要写一些表单来收集数据,我们后端设计的有公告的标题、公告的内容、类型、状态等操作。我看了一些其他的博客和现实的项目设计,大多数是我们写好公告内容,然后点击发布公告按钮,就会弹出一个提示框,然后放一些关于公告的信息之类的表单需要我们去填写,然后再点击提交提交到后台数据交互。

我们接下来也是按照这个思路来写页面,我们先设计我们的页面,现在页面上就一个输入的编辑器,我们还需要在头部加一个公告的标题表单和一个发布公告的按钮。

    <!-- 发布文章 -->
    <div class="article-title-container">
        <el-input size="medium"  v-model="notice.noticeTitle" placeholder="输入公告标题"/>
        <el-button type="danger" size="medium" @click="openDialog" style="margin-left:10px">发布公告</el-button>
    </div>

 data() {
    return {
      notice: {
        noticeContent: "",
        noticeTitle: "",
      }
    }
  },

css样式:

.article-title-container {
  display: flex;
  align-items: center;
  margin-bottom: 1.25rem;
  margin-top: 1.25rem;
}

我们去看一下效果:

在这里插入图片描述 看,现在是不是写的有点样子了,下面我们重点写点击发布公告的功能。

在上边我们写发布公告按钮的时候,大家注意到我绑定了一个点击事件,当我点击这个发布公告按钮的时候弹出对话框,之前我们也写过弹出框。

我们先写一个弹出框。

<el-dialog :title="`发布公告:${notice.noticeTitle}`" :visible.sync="showDialog" width="30%">
 
  <span slot="footer">
     <el-button @click="handleCancel">取 消</el-button>
     <el-button type="primary" @click="handleSubmit">发布</el-button>
  </span>
</el-dialog>

然后在data中控制展示的开关showDialog,默认是不展示。

 data() {
    return {
      showDialog: false,
      notice: {
        noticeContent: "",
        noticeTitle: "",        
      }
    }
  },

然后我们实现openDialog这个点击事件。这里我做了一下验证,我们页面上的标题和内容如果为空的话,就不允许打开弹出框。不为空则将showDialog设置为true。

   // 打开文章信息填写框
    openDialog() {
      if (
        this.assertNotEmpty(this.notice.noticeTitle, "请填写公告标题") &&
        this.assertNotEmpty(this.notice.noticeContent, "请填写公告内容")
      ) {
        this.showDialog = true;
      }
    },

   assertNotEmpty(target, msg) {
      if (!target) {
        this.$message({
          message: msg,
          type: "warning",
        });
        return false;
      }
      return true;
    },

此时我们来测试一下。打开页面,当我们不输入标题和内容时,会有这个提示信息。

在这里插入图片描述 当我们输入好信息之后,再点击发布公告。

在这里插入图片描述 此时点击弹出框的取消没有效果,我们再写一个取消的方法handleCancel。

handleCancel() {
    this.showDialog = false;
},

还有一个弹框中右下角还有一个发布按钮,这个我们将信息全部提交到后端接口中,这个我们稍后再写。 先写弹出框中的表单。

 <el-form label-position="left" label-width="80px">
        <el-form-item label="公告类型">
          <el-select v-model="notice.noticeType" placeholder="请选择公告类型">
            <el-option label="公告" :value="0"></el-option>
            <el-option label="通知" :value="1"></el-option>
            <el-option label="提醒" :value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="状态">
          <el-switch v-model="notice.noticeStatus"></el-switch>
        </el-form-item>
 </el-form>

<!-- 添加类型和状态 -->
 data() {
    return {
      showDialog: false,
      notice: {
        noticeContent: "",
        noticeTitle: "",
        noticeType: "",
        noticeStatus: true
      }
    }
  },

这个就是一个下拉框和按钮,大家看一下element文档就能理解了,很简单。

接下来我们写一下提交的按钮实现的方法,将添加公告的接口引进来。这个也很简单,我们之前也写了很多遍了,相信大家都可以看的懂。

import { addNotice } from '@/api/notice'

handleSubmit() {
        this.showDialog = true;
        var body = this.notice;
        addNotice(body).then((response) => {
           this.$notify({
            title: "提示",
            message: `公告《${this.notice.noticeTitle}》发布成功`,
            type: "success",
          });
           this.$router.push("/notice/list");
        }) 
},

效果如下:

在这里插入图片描述 好啦,前端的发布公告到这里就写完了,此时我们需要如修改后端,当初设计的时候不是特别的完整,所以我们要修改一下后端的参数和部分功能代码。

1、修改字段 先修改一下数据库,之前我们设计的字段有些不太合理,现在我们修改一下。

DROP TABLE IF EXISTS `person_notice`;
CREATE TABLE `person_notice` (
 `notice_id`            INT             NOT NULL PRIMARY KEY AUTO_INCREMENT     COMMENT '主键',
 `notice_title`		    VARCHAR(255)    NOT NULL                                COMMENT '公告标题',
 `notice_type`		    TINYINT    	    NOT NULL DEFAULT '0'                    COMMENT '公告类型,默认0, 0-公告, 1-通知, 2-提醒',
 `notice_status`        tinyint(4)      NOT NULL DEFAULT '0'                    COMMENT '状态,默认0, 0-正常, 1-关闭',
 `notice_content`       text                NULL                                COMMENT '公告内容',
 `create_by`   		    VARCHAR(128)    NOT NULL                                COMMENT '创建者',
 `create_time`          DATETIME            NULL DEFAULT CURRENT_TIMESTAMP      COMMENT '创建时间',
 `update_time`          DATETIME            NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
) ENGINE = InnoDB
  CHARACTER SET = utf8mb4
  COLLATE = utf8mb4_bin
  ROW_FORMAT = Dynamic
  COMMENT '通知公告表';

字段修改了,我们的实体类也要修改

package com.blog.personalblog.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 通知公告
 */

@Data
public class Notice {

    /**
     * 主键
     */
    private Integer noticeId;

    /**
     * 公告标题
     */
    private String noticeTitle;

    /**
     * 公告类型,默认0, 0-公告, 1-通知, 2-提醒
     */
    private Integer noticeType;

    /**
     * 状态,默认0, true-正常, false-关闭
     */
    private Boolean noticeStatus;

    /**
     * 公告内容
     */
    private String noticeContent;

    /**
     * 创建者
     */
    private String createBy;

    /**
     * 创建时间
     */
    @JsonFormat(timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @JsonFormat(timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

}

字段修改了我们就要去修改xml文件中的sql语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.blog.personalblog.mapper.NoticeMapper">
    <resultMap id="BaseResultMap" type="com.blog.personalblog.entity.Notice">
        <result column="notice_id" jdbcType="INTEGER" property="noticeId"/>
        <result column="notice_title" jdbcType="VARCHAR" property="noticeTitle"/>
        <result column="notice_type" jdbcType="INTEGER" property="noticeType"/>
        <result column="notice_status" jdbcType="INTEGER" property="noticeStatus"/>
        <result column="notice_content" jdbcType="VARCHAR" property="noticeContent"/>
        <result column="create_by" jdbcType="VARCHAR" property="createBy"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
    </resultMap>

    <select id="getNoticePage" resultMap="BaseResultMap">
        select * from person_notice
    </select>

    <insert id="createNotice" parameterType="com.blog.personalblog.entity.Notice" useGeneratedKeys="true" keyProperty="noticeId">
        INSERT INTO person_notice (notice_title, notice_type, notice_status, notice_content, create_by)
        VALUES(#{noticeTitle}, #{noticeType}, #{noticeStatus}, #{noticeContent}, #{createBy})
    </insert>

    <update id="updateNotice" parameterType="com.blog.personalblog.entity.Notice">
        update person_notice
        <set>
            notice_title = #{noticeTitle},
            notice_type = #{noticeType},
            notice_status = #{noticeStatus},
            notice_content = #{noticeContent},
            create_by = #{createBy}
        </set>
        WHERE notice_id = #{noticeId}
    </update>

    <delete id="deleteNotice" parameterType="java.lang.Integer">
        delete from person_notice where notice_id = #{noticeId, jdbcType=INTEGER}
    </delete>


</mapper>

再去Controller层修改一下接口,我们要获得当前发布公告的人员。

  /**
     * 添加公告
     * @return
     */
    @ApiOperation(value = "添加公告")
    @PostMapping("/create")
    @OperationLogSys(desc = "添加公告", operationType = OperationType.INSERT)
    public JsonResult<Object> noticeCreate(@RequestBody @Valid Notice notice) {
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        notice.setCreateBy(username);
        int isStatus = noticeService.saveNotice(notice);
        if (isStatus == 0) {
            return JsonResult.error("添加公告失败");
        }
        return JsonResult.success();
    }

都写完之后,别忘了更新数据库,然后我们将项目跑起来,测试一下。

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 这里我们需要将公告内容作为不展示吧,有可能内容比较长,展示不太美观。 好啦,下面还有一个编辑功能,接下来我们来写一下编辑公告。

4、编辑公告

打开公告的列表,我们在编辑的按钮上加上一个跳转到添加的页面上。

<el-button type="primary" size="mini" icon="el-icon-edit" @click="$router.push({name: 'AddNotice', params: { noticeId: scope.row.noticeId }})">编辑</el-button>

这里用到了一个$router.push路由传值 ,在我们点击编辑的时候,会跳转到添加页面上,通过带着公告id一起传过去。获取参数的两种常用方法:params和query

这里需要注意的是:由于动态路由也是传递params的,所以在this.$router.push()方法中path不能和params一起使用,否则params将无效。需要用name来指定页面。及通过路由配置的name属性访问。

$router.push({name: 'AddNotice', params: { noticeId: scope.row.noticeId }}) 这里面的name的值就是我们要跳转页面路由的名称,在router文件中我圈起来的name

在这里插入图片描述 下面我们要去发布公告的add.vue中去接收处理。

在add.vue中,上边的表单我们不需要改动,只需要将接收过来的数据进行处理即可。

我们写一个生命周期函数钩子created(),一个Vue实例被生成后会调用这个函数。我们这里面写检查是否存在noticeId,如果有值则获取文章的信息,我们这里还差一个根据公告id获取公告内容的接口,我们先将这个方法完成,再去后端写这个接口。

import { addNotice, updateNotice, getNoticeById } from '@/api/notice'

created() {
    const noticeId = this.$route.params.noticeId;
    if(noticeId) {
      getNoticeById(noticeId).then((res) => {
        this.notice = res.data;
      });
    }

  },

先将修改和根据id查询公告的接口引入进来,我们后端还没实现这个接口,我先把这个说完再去写,api的文件中还要添加一下接口。

我们根据this.$route.params.noticeId获取到点击编辑传过来的id,然后如果不为空的话,则去调getNoticeById接口,获取公告信息。

我们再去api/notice.js文件中添加这接口。

export function getNoticeById(id){
  return request({
    url: '/notice/info/' + id,
    method: 'get'
  })
}

好啦,我们去后端将这个接口实现一下。 对于现在的我们来说,写个接口已经是轻车熟路了,先写service接口层

    /**
     * 根据公告id获取公告
     * @param noticeId
     * @return
     */
    Notice getNoticeById(Integer noticeId);

实现类:

    @Override
    public Notice getNoticeById(Integer noticeId) {
        Notice notice = noticeMapper.getNoticeById(noticeId);
        return notice;
    }

数据接口dao层

    /**
     * 获取公告
     * @param id
     * @return
     */
    Notice getNoticeById(Integer id);

mybatis的xml

    <select id="getNoticeById" resultMap="BaseResultMap">
        select * from person_notice where notice_id = #{noticeId}
    </select>

最后写一下Controller层

    @GetMapping("/info/{id}")
    @ApiOperation("根据id查询公告信息")
    @OperationLogSys(desc = "查询公告", operationType = OperationType.SELECT)
    public JsonResult<Object> getNotice(@PathVariable Integer id) {
        Notice notice = noticeService.getNoticeById(id);
        return JsonResult.success(notice);
    }

后端完成之后,我们再去写一下前端的发布公告的功能。在return的方法中再加一个noticeId

data() {
    return {
      showDialog: false,
      notice: {
        noticeContent: "",
        noticeTitle: "",
        noticeType: "",
        noticeStatus: true,
        noticeId: ""
      }
    }
  },

由于我们添加和修改使用的是同一个方法,所以我们在提交发布公告的时候要去判断一下我们是更新还是添加公告,这个和之前的分类功能基本上差不多。

 handleSubmit() {
        this.showDialog = true;
        var body = this.notice;
        if(this.notice.noticeId) {
          updateNotice(body).then((res) => {
            this.$notify({
              title: "提示",
              message: `公告《${this.notice.noticeTitle}》更新成功`,
              type: "success",
            });
            this.$router.push("/notice/list");
          });
        } else {
            addNotice(body).then((response) => {
              this.$notify({
                title: "提示",
                message: `公告《${this.notice.noticeTitle}》发布成功`,
                type: "success",
              });
              this.$router.push("/notice/list");
            })
        }
      
    },

到这里我们的公告基本上写完了,我们来测试一下功能。

在这里插入图片描述 点击编辑完之后,这边数据就会自动填充过来了,说明获取数据没有问题

在这里插入图片描述

点击发布公告。

在这里插入图片描述

这边数据也都有了,我们修改一下类型改成通知。

在这里插入图片描述 好啦,此时已经修改好了,我们的公告管理也实现了。

5、修改bug

在查看公告列表的时候,我发现公告的状态始终是关闭的状态,是由于我们在写列表的时候字段还没修改,所以我们去修改一下展示的字段。

  <el-table-column align="center" label="状态" >
        <template slot-scope="scope">
          <span>{{ noticeStatus(scope.row.noticeStatus) }}</span>
        </template>
   </el-table-column>

noticeStatus方法也要修改一下

noticeStatus(val) {
      if (val) {
        return "正常";
      } else {
        return "关闭";
      }
   },

这时我们再去看一下页面,修改一下状态可以发现完美实现了。

在这里插入图片描述 设置公告的状态是关闭还是打开,这个主要是关乎到公告是否在前端页面展示,我们这个是后台的管理系统,对公告可以控制页面展不展示该公告。

list.vue文件的完整代码:

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

    <!-- 设置标题公告管理 -->
    <div slot="header" class="clearfix">
      <span>公告列表</span>
    </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.noticeId }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="公告标题" >
        <template slot-scope="scope">
          <span>{{ scope.row.noticeTitle }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="公告类型" >
        <template slot-scope="scope">
          <span>{{filterStatus( scope.row.noticeType )}}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="状态" >
        <template slot-scope="scope">
          <span>{{ noticeStatus(scope.row.noticeStatus) }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="创建者" >
        <template slot-scope="scope">
          <span>{{ scope.row.createBy }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="创建时间" >
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.createTime }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="更新时间" >
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.updateTime }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" icon="el-icon-edit" @click="$router.push({name: 'AddNotice', params: { noticeId: scope.row.noticeId }})">编辑</el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteNoticeById(scope.row.noticeId)" >删除</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 { noticeList, deleteNotice } from '@/api/notice'

export default {
  name: 'noticeList',

  created() {
    this.getList()
  },

  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
    }
  },

  methods: {

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

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

    filterStatus(val) {
      if (val == "0") {
        return "公告";
      } else if (val == "1") {
        return "通知";
      } else {
        return "提醒";
      }
    },
    noticeStatus(val) {
      if (val) {
        return "正常";
      } else {
        return "关闭";
      }
    },

    deleteNoticeById (id) {
      this.$confirm('此操作将永久删除该公告, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteNotice(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;
  }
</style>

add.vue文件的完整代码:

<template>
    <el-card class="box-card">
    <!-- 设置标题公告管理 -->
    <div slot="header" class="clearfix">
      <span>添加公告</span>
    </div>

    <!-- 发布文章 -->
    <div class="article-title-container">
        <el-input size="medium"  v-model="notice.noticeTitle" placeholder="输入公告标题"/>
        <el-button type="danger" size="medium" @click="openDialog" style="margin-left:10px">发布公告</el-button>
    </div>
    <!-- 公告内容 -->
    <mavon-editor ref="md" v-model="notice.noticeContent" style="height:calc(100vh - 260px)"/>

    <!-- 填写信息弹出框 -->
    <el-dialog :title="`发布公告:${notice.noticeTitle}`" :visible.sync="showDialog" width="30%">

      <el-form label-position="left" label-width="80px">
        <el-form-item label="公告类型">
          <el-select v-model="notice.noticeType" placeholder="请选择公告类型">
            <el-option label="公告" :value="0"></el-option>
            <el-option label="通知" :value="1"></el-option>
            <el-option label="提醒" :value="2"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="状态">
          <el-switch v-model="notice.noticeStatus"></el-switch>
        </el-form-item>
      </el-form>

       <span slot="footer">
        <el-button @click="handleCancel">取 消</el-button>
        <el-button type="primary" @click="handleSubmit">发布</el-button>
      </span>
    </el-dialog>


 </el-card>
</template>

<script>
import { addNotice, updateNotice, getNoticeById } from '@/api/notice'

export default {
  name: 'Addnotice',

  created() {
    const noticeId = this.$route.params.noticeId;
    if(noticeId) {
      getNoticeById(noticeId).then((res) => {
        this.notice = res.data;
      });
    }

  },

   data() {
    return {
      showDialog: false,
      notice: {
        noticeContent: "",
        noticeTitle: "",
        noticeType: "",
        noticeStatus: true,
        noticeId: ""
      }
    }
  },

  methods: {

    assertNotEmpty(target, msg) {
      if (!target) {
        this.$message({
          message: msg,
          type: "warning",
        });
        return false;
      }
      return true;
    },

     // 打开文章信息填写框
    openDialog() {
      if (
        this.assertNotEmpty(this.notice.noticeTitle, "请填写公告标题") &&
        this.assertNotEmpty(this.notice.noticeContent, "请填写公告内容")
      ) {
        this.showDialog = true;
      }
    },

    handleSubmit() {
        this.showDialog = true;
        var body = this.notice;
        if(this.notice.noticeId) {
          updateNotice(body).then((res) => {
            this.$notify({
              title: "提示",
              message: `公告《${this.notice.noticeTitle}》更新成功`,
              type: "success",
            });
            this.$router.push("/notice/list");
          });
        } else {
            addNotice(body).then((response) => {
              this.$notify({
                title: "提示",
                message: `公告《${this.notice.noticeTitle}》发布成功`,
                type: "success",
              });
              this.$router.push("/notice/list");
            })
        }
      
    },
    
    handleCancel() {
      this.showDialog = false;
    },
  }
 
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>

.article-title-container {
  display: flex;
  align-items: center;
  margin-bottom: 1.25rem;
  margin-top: 1.25rem;
}

  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
</style>

三、标签管理

接下来我们再完成标签管理,标签管理基本上和分类差不多,我们可以参照分类进行编写,大家可以先去写再回过来看看我写的。

这里基本上和分类一致,我不再一一的讲解了,我将代码贴出来,作为大家的参考代码。其中修改我们后端的请求方式是put,在前端的api中要以put方式请求,删除为delete请求,这个删除我们不再修改后端的代码,直接在请求地址后边拼接一个id即可,大家再写的时候注意一下,其余的没什么多余的了。

1、tag.js接口

import request from '@/utils/request'

export function tagList(query) {
    return request({
      url: '/tag/list', 
      method: 'post',
      data: query
    })
}

export function addTag(data) {
    return request({
      url: '/tag/create',
      method: 'post',
      data
    })
}

export function updateTag(data) {
    return request({
      url: '/tag/update',
      method: 'put',
      data
    })
}

export function deleteTag(id) {
  return request({
    url: '/tag/delete/' + id,
    method: 'delete',
  })
}

2、list.vue页面

<template>
  <el-card class="box-card">
    <el-button type="primary" size="small" icon="el-icon-plus" @click="transformation(null)">新增标签</el-button>

    <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 align="center" label="标签名称">
        <template slot-scope="scope">
          <span>{{ scope.row.tagName}}</span>
        </template>
      </el-table-column>

       <el-table-column align="center" label="创建时间">

        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.createTime}}</span>
        </template>
      </el-table-column>

       <el-table-column align="center" label="更新时间">
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.updateTime}}</span>
        </template>
      </el-table-column>

      
      <el-table-column align="center" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" icon="el-icon-edit" @click="transformation(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteTagById(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-dialog :visible.sync="addOrupdateDialogVisible" width="30%">
      <div class="dialog-title-container" slot="title" ref="tagTitle" />
      <el-form label-width="100px" size="medium" :model="tagForm">
        <el-form-item label="标签名称:">
          <el-input v-model="tagForm.tagName" style="width:220px" />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="addOrupdateDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="addTag">
          确 定
        </el-button>
      </div>
    </el-dialog>
   
  </el-card>
</template>

<script>
  import { tagList, addTag, updateTag, deleteTag } from '@/api/tag'

export default {
  name: 'TagList',

  created() {
    this.getList()
  },

  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
      addOrupdateDialogVisible: false,
      tagForm: {
        id: null,
        tagName: ""
      },
    }
  },

  methods: {

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

    transformation(tag) {
      if (tag != null) {
        this.tagForm = JSON.parse(JSON.stringify(tag));
        this.$refs.tagTitle.innerHTML = "修改标签";
      } else {
        this.tagForm.id = null;
        this.tagForm.tagName = "";
        this.$refs.tagTitle.innerHTML = "添加标签";
      }
      this.addOrupdateDialogVisible = true;
    },

    addTag() {
      if (this.tagForm.tagName.trim() == "") {
        this.$message.error("标签名不能为空");
        return false;
      }
      var body = this.tagForm;
      if(body.id == null){
        addTag(body).then(response => {
          this.$message({
            type: 'success',
            message: '添加标签成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      } else {
        updateTag(body).then(response => {
          this.$message({
            type: 'success',
            message: '修改标签成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      }
      this.addOrupdateDialogVisible = false;
    },

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

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

  }

}

</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;
  }
</style>

在这里插入图片描述 写完之后,跑起来测一下增删改查即可,如有bug大家给我留言。

四、总结

好啦!这次就写到这里吧,熬了几夜终于搞完了,大家打开自己的系统是不是有点成就感了,或许这就是梦想开始的起点

我们还有控制台、文章管理两大块没有写,下一篇基本是可以结束了,但不排除我再加点新的东西,哈哈。小伙伴们加油吧,走到这一步不容易,你会为你的坚持感到骄傲。

上一篇:Spring Boot + vue-element 开发个人博客项目实战教程(二十、登录日志、用户、分类管理页面开发)

下一篇:Spring Boot + vue-element 开发个人博客项目实战教程(二十二、待定)