废弃说明:
这个专栏的文章本意是记录笔者第一次搭建博客的过程,文章里里有很多地方的写法都不太恰当,现在已经废弃,关于SpringBoot + Vue 博客系列,笔者重新写了这个系列的文章,不敢说写的好,但是至少思路更加清晰,还在看SpringBoot + Vue 博客系列文章的朋友可以移步:https://blog.csdn.net/li3455277925/category_10341110.html,文章中有错误的地方或者大家有什么意见或建议都可以评论或者私信交流。

需求

博客给浏览者提供留言功能,可以是指出博主文章的错误,也可以是关于博客设计上的不足。未登录的用户可以进行匿名留言,匿名留言展示默认头像和默认昵称。

实现

后端数据库设计

与数据库中留言表对应实体类设计如下:

package com.qianyucc.blog.model.entity;

import lombok.*;
import javax.persistence.*;

/**
 * @author lijing
 * @date 2019-10-17 16:41
 * @description 留言实体类
 */
@Data
@Entity
@Table(name = "message")
public class MessageDO {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long creator;
    private String content;
    @Column(name = "gmt_update")
    private Long gmtCreate;
}

数据库访问层设计

package com.qianyucc.blog.repository;

import com.qianyucc.blog.model.entity.*;
import org.springframework.data.jpa.repository.*;

/**
 * @author lijing
 * @date 2019-10-17 16:45
 * @description 留言数据库访问层
 */
public interface MessageRepository  extends JpaRepository<MessageDO, Long>, JpaSpecificationExecutor<MessageDO>{
}

业务层主要实现留言插入和查询功能:

package com.qianyucc.blog.service;

import com.qianyucc.blog.model.dto.*;
import com.qianyucc.blog.model.entity.*;
import com.qianyucc.blog.model.vo.*;
import com.qianyucc.blog.repository.*;
import com.qianyucc.blog.utils.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;

import java.time.*;
import java.util.*;
import java.util.stream.*;

/**
 * @author lijing
 * @date 2019-10-17 16:47
 * @description 与留言相关的业务
 */
@Service
public class MessageService {
    @Autowired
    private MessageRepository messageRepository;
    @Autowired
    private UserRepository userRepository;

    /**
     * 插入一条留言
     *
     * @param messageDTO
     */
    public void insMessage(MessageDTO messageDTO) {
        MessageDO messageDO = new MessageDO();
        messageDO.setCreator(messageDTO.getCreatorId());
        messageDO.setContent(messageDTO.getContent());
        messageDO.setGmtCreate(Instant.now().toEpochMilli());
        messageRepository.save(messageDO);
    }

    /**
     * 查询所有留言信息
     *
     * @return
     */
    public List<MessageVO> findAllMessages() {
        List<MessageDO> messageDOS = messageRepository.findAll();
        List<MessageVO> messageVOS = messageDOS.stream()
                .sorted(Comparator.comparing(MessageDO::getGmtCreate).reversed())
                .map(messageDO -> {
                    MessageVO messageVO = new MessageVO();
                    messageVO.setCreatorId(messageDO.getCreator());
                    Optional<UserDO> byId = userRepository.findById(messageDO.getCreator());
                    byId.ifPresent(m -> {
                        messageVO.setCreatorName(m.getName());
                        messageVO.setAvatarUrl(m.getAvatarUrl());
                    });
                    messageVO.setContent(messageDO.getContent());
                    messageVO.setGmtCreate(BlogUtil.formatDate(messageDO.getGmtCreate(), "yyyy年MM月dd日 HH:mm"));
                    return messageVO;
                })
                .collect(Collectors.toList());
        return messageVOS;
    }
}

此处用到MessageVO来封装发送给前端的信息

package com.qianyucc.blog.model.vo;

import lombok.*;

/**
 * @author lijing
 * @date 2019-10-17 16:42
 * @description 封装向页面返回的留言信息
 */
@Data
public class MessageVO {
    private Long creatorId;
    private String creatorName;
    private String avatarUrl;
    private String content;
    private String gmtCreate;
}

设计Controller

package com.qianyucc.blog.controller.comm;

import com.qianyucc.blog.model.dto.*;
import com.qianyucc.blog.model.vo.*;
import com.qianyucc.blog.service.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * @author lijing
 * @date 2019-10-17 16:46
 * @description 留言信息api
 */
@RestController
@RequestMapping("/api/comm/message")
public class MessageController {
    @Autowired
    private MessageService messageService;

    @GetMapping("/getMessages")
    public List<MessageVO> getMessages() {
        List<MessageVO> messages = messageService.findAllMessages();
        return messages;
    }

    @PostMapping("/leaveAMessage")
    public RespDataVO leaveAMessage(@RequestBody MessageDTO messageDTO) {
        messageService.insMessage(messageDTO);
        return RespDataVO.ok("留言成功!");
    }
}

其中MessageDTO的设计如下:

package com.qianyucc.blog.model.dto;

import lombok.*;

/**
 * @author lijing
 * @date 2019-10-17 16:52
 * @description 前端传递的留言信息
 */
@Data
public class MessageDTO {
    private Long creatorId;
    private String content;
}
前端实现

/src/request/api/url.js中添加对应URL

// message
const getMessagesUrl = baseUrl + 'api/comm/message/getMessages'
const leaveAMessageUrl = baseUrl + 'api/comm/message/leaveAMessage'

新建/src/request/api/message.js文件,用来封装与留言相关的api

// 导入axios实例
import axios from "@/request/http"
// 导入所有url
import url from '@/request/api/url'

export default {
  getMessages(callback) {
    axios
      .get(url.getMessagesUrl)
      .then(callback)
      .catch(err => {
        console.log("getMessages Error");
      })
  },
  leaveAMessage(message, callback) {
    axios
      .post(url.leaveAMessageUrl, message)
      .then(callback)
      .catch(err => {
        console.log("leaveAMessage Error");
      })
  }
}

不要忘了在/src/request/api/index.js中导入message
导入message
新建/src/components/page/leaveMessage.vue组件,展示留言,并提供留言功能:

<template>
  <b-container class="main">
    <h5>说点什么吧......</h5>
    <hr />

    <b-media>
      <template v-slot:aside>
        <b-img
          rounded="circle"
          width="60"
          height="60"
          :src="isLogin == true ? userInfo.avatarUrl : '/static/images/no-name.png'"
        ></b-img>
      </template>
      <h6 thumbnail v-text="isLogin == true ? userInfo.name : '匿名用户'"></h6>
      <b-textarea rows="5" placeholder="请输入留言内容......" v-model="message"></b-textarea>
      <b-button class="pull-right" variant="success" @click="leaveAMessage()">提交</b-button>
    </b-media>
    <hr />
    <marquee direction="down" behaviour="scroll" height="300px">
      <b-media v-for="(m,index) in messages" :key="index" class="message">
        <template v-slot:aside>
          <b-img
            rounded="circle"
            width="60"
            height="60"
            :src="m.creatorId == 0 ? '/static/images/no-name.png' : m.avatarUrl"
          ></b-img>
        </template>
        <h6 thumbnail v-text="m.creatorId == 0 ? '匿名用户' : m.creatorName"></h6>
        <p>{{m.content}}</p>
        <small>{{m.gmtCreate}}</small>
      </b-media>
    </marquee>
  </b-container>
</template>

<script>
import { mapState } from "vuex";
export default {
  name: "leaveMessage",
  data() {
    return {
      messages: [],
      message: ""
    };
  },
  computed: {
    ...mapState({
      isLogin: state => state.app.isLogin,
      userInfo: state => state.user.userInfo
    })
  },
  methods: {
    leaveAMessage() {
      if (this.message.trim() != "") {
        let messageInfo = {
          creatorId: this.isLogin ? this.userInfo.id : 0,
          content: this.message
        };
        this.$api.message.leaveAMessage(messageInfo);
        this.message = "";
        this.getMessages();
      }
    },
    getMessages() {
      this.$api.message.getMessages(resp => {
        this.messages = resp.data;
      });
    }
  },
  created() {
    this.getMessages();
  }
};
</script>

<style scoped>
.main {
  margin-top: 10px;
  padding: 20px;
  background-color: #fff;
  min-height: 100%;
}
textarea {
  margin-bottom: 10px;
}
h6 {
  font-weight: bold;
}
.pull-right {
  float: right;
}
.message {
  border: solid rgb(239, 239, 239) 1px;
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 10px;
}
</style>

上面代码用到了<marquee></marquee>标签,可实现滚动效果

在路由中添加leaveMessage

{
  path: "/leaveMessage",
  name: "leaveMessage",
  component: leaveMessage
},

修改导航栏,点击“留言”跳转到leaveMessage组件

<b-nav-item to="/leaveMessage">留言</b-nav-item>
页面效果

留言功能效果展示

Logo

前往低代码交流专区

更多推荐