SpringBoot+Vue豆宝社区前后端分离项目手把手实战系列教程15---实现发帖功能(MarkDown编辑器整合)

369 阅读1分钟

豆宝社区项目实战教程简介

本项目实战教程配有免费视频教程,配套代码完全开源。手把手从零开始搭建一个目前应用最广泛的Springboot+Vue前后端分离多用户社区项目。本项目难度适中,为便于大家学习,每一集视频教程对应在Github上的每一次提交。

项目首页截图

image

代码开源地址

前端 后端

视频教程地址

视频教程

前端技术栈

Vue Vuex Vue Router Axios Bulma Buefy Element Vditor DarkReader

后端技术栈

Spring Boot Mysql Mybatis MyBatis-Plus Spring Security JWT Lombok

文章发表前端(vditor)

1.安装vditor组件

yarn add vditor

2.api

src\api\post.js中添加

// 发布
export function post(topic) {
  return request({
    url: '/post/create',
    method: 'post',
    data: topic
  })
}

3.路由

src\router\index.js中添加

// 发布
  {
    name: "post-create",
    path: "/post/create",
    component: () => import("@/views/post/Create"),
    meta: { title: "信息发布", requireAuth: true },
  }

4.新增Create页面

src\views\post\创建Create.vue

<template>
  <div class="columns">
    <div class="column is-full">
      <el-card
        class="box-card"
        shadow="never"
      >
        <div
          slot="header"
          class="clearfix"
        >
          <span><i class="fa fa fa-book"> 主题 / 发布主题</i></span>
        </div>
        <div>
          <el-form
            ref="ruleForm"
            :model="ruleForm"
            :rules="rules"
            class="demo-ruleForm"
          >
            <el-form-item prop="title">
              <el-input
                v-model="ruleForm.title"
                placeholder="输入主题名称"
              />
            </el-form-item>

            <!--Markdown-->
            <div id="vditor" />

            <b-taginput
              v-model="ruleForm.tags"
              class="my-3"
              maxlength="15"
              maxtags="3"
              ellipsis
              placeholder="请输入主题标签,限制为 15 个字符和 3 个标签"
            />

            <el-form-item>
              <el-button
                type="primary"
                @click="submitForm('ruleForm')"
              >立即创建
              </el-button>
              <el-button @click="resetForm('ruleForm')">重置</el-button>
            </el-form-item>
          </el-form>
        </div>
      </el-card>
    </div>
  </div>
</template>

<script>
import { post } from '@/api/post'
import Vditor from 'vditor'
import 'vditor/dist/index.css'
export default {
  name: 'TopicPost',
  data() {
    return {
      contentEditor: {},
      ruleForm: {
        title: '', // 标题
        tags: [], // 标签
        content: '' // 内容
      },
      rules: {
        title: [
          { required: true, message: '请输入话题名称', trigger: 'blur' },
          {
            min: 1,
            max: 25,
            message: '长度在 1 到 25 个字符',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  mounted() {
    this.contentEditor = new Vditor('vditor', {
      height: 500,
      placeholder: '此处为话题内容……',
      theme: 'classic',
      counter: {
        enable: true,
        type: 'markdown'
      },
      preview: {
        delay: 0,
        hljs: {
          style: 'monokai',
          lineNumber: true
        }
      },
      tab: '\t',
      typewriterMode: true,
      toolbarConfig: {
        pin: true
      },
      cache: {
        enable: false
      },
      mode: 'sv'
    })
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          if (
            this.contentEditor.getValue().length === 1 ||
            this.contentEditor.getValue() == null ||
            this.contentEditor.getValue() === ''
          ) {
            alert('话题内容不可为空')
            return false
          }
          if (this.ruleForm.tags == null || this.ruleForm.tags.length === 0) {
            alert('标签不可以为空')
            return false
          }
          this.ruleForm.content = this.contentEditor.getValue()
          post(this.ruleForm).then((response) => {
            const { data } = response
            setTimeout(() => {
              this.$router.push({
                name: 'post-detail',
                params: { id: data.id }
              })
            }, 800)
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    resetForm(formName) {
      this.$refs[formName].resetFields()
      this.contentEditor.setValue('')
      this.ruleForm.tags = ''
    }
  }
}
</script>

<style>
</style>

5.测试页面

image-20210213153111929

文章发表后端

DTO

import lombok.Data;
import java.io.Serializable;
import java.util.List;

@Data
public class CreatePostDTO implements Serializable {
    private static final long serialVersionUID = -5957433707110390852L;

    /**
     * 标题
     */
    private String title;

    /**
     * 内容
     */
    private String content;

    /**
     * 标签
     */
    private List<String> tags;

}

BmsPostController

/**
 * 创建文章
 *
 * @return
 */
@PostMapping("/create")
public ApiResult<Page<PostVO>> createPost(
        @RequestHeader(value = "userName") String userName,
        @RequestBody CreatePostDTO createPostDTO) {
    BmsPost post = postService.createPost(userName, createPostDTO);
	return ApiResult.success(post);
}

BmsPostService

/**
 * 保存文章
 * @param userName
 * @param createPostDTO
 */
@Transactional(rollbackFor = RuntimeException.class)
public BmsPost createPost(String userName, CreatePostDTO createPostDTO) {
    
    // 获取用户
    UmsUser loginUser = userService.getOne(
            new LambdaQueryWrapper<UmsUser>().eq(UmsUser::getUsername, userName)
    );
    if (ObjectUtils.isNull(loginUser)) {
        ApiAsserts.fail("用户不存在");
    }
    
    // 保存
    BmsPost post = BmsPost.builder()
            .userId(loginUser.getId())
            .title(createPostDTO.getTitle())
            .content(createPostDTO.getContent())
            .createTime(new Date())
            .build();
    this.save(post);
    // 给用户增加积分+1
    userService.updateById(loginUser.setScore(loginUser.getScore() + 1));
    
    // 保存标签(先查询是否存在)
    for (String tag : createPostDTO.getTags()) {
        BmsTag bmsTag = tagMapper.selectOne(
                new LambdaQueryWrapper<BmsTag>().eq(BmsTag::getName, tag)
        );
        //如果不存在,保存
        if (ObjectUtils.isNull(bmsTag)){
            bmsTag = new BmsTag();
            bmsTag.setName(tag);
            bmsTag.setPostCount(1);
            tagMapper.insert(bmsTag);
            System.out.println("bmsTag = " + bmsTag);
        }else{// 否存 + 1
            tagMapper.updateById(bmsTag.setPostCount(bmsTag.getPostCount()+1));
        }
        // 保存post_tag
        System.out.println("post.getId() = " + post.getId());
        System.out.println("bmsTag.getId() = " + bmsTag.getId());
        postTagService.save(BmsPostTag.builder().postId(post.getId()).tagId(bmsTag.getId()).build());
        
    }
            return post;
}