豆宝社区项目实战教程简介
本项目实战教程配有免费视频教程,配套代码完全开源。手把手从零开始搭建一个目前应用最广泛的Springboot+Vue前后端分离多用户社区项目。本项目难度适中,为便于大家学习,每一集视频教程对应在Github上的每一次提交。
项目首页截图
代码开源地址
视频教程地址
前端技术栈
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.测试页面
文章发表后端
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;
}