Vue+SpringBoot 前后端分离实战(mybatisplus多表分页查询+博客显示)

前言

到今天总算是把最基本的功能做出来了,前面修修改改了不少主要是前端先前有点小毛病,调试花费了不少时间。之后就是关于在mybatisplus里面使用纯面对对象的方式来实现多表查询,我测试了最后决定还是手写sql香,对于稍微复杂一点的查询(多表),如果采用纯面对对象的写法的话有点难,而且很麻烦,还不如直接用 xml文件映射过去。除此之外修复了几个小bug

对博客封面的检测

这个是先前偷懒没有去做,后面才发现的,其实我也是忘了,有些网站是不允许,js加载图片的,由于vue其实使用js动态加载图片的所以,有些网站有防护机制,所以我这边就必须检测一下那个图片是否可以被加载。这很重要,不然显示不出来。
这里上传博客的前端代码修改如下(其实主要还是前端)



<template>
  <div class="m_container">

    <!-- 博客内容 -->
    <div class="m_content">
      <el-form ref="editForm" status-icon :model="editForm"   label-width="80px">

        <input type="text" name="blogname" placeholder="请输入文章标题" v-model="editForm.title">
        <el-button  id="submit" type="primary" @click="tosubmitForm('editForm')">发布文章</el-button>


        <br>
        <br>
        <mavon-editor
          v-model="editForm.content"
          ref="md"
          @imgAdd="imgAdd"
          @change="change"
          style="min-height: 800px;width: 100%"
        />

      </el-form>
    </div>

    <!-- 对话框内容 -->
    <el-dialog title="发布文章" :visible.sync="dialogFormVisible" width="35%">
      <el-form :model="editForm" ref="editForm2">

        <el-form-item label="描述" prop="description">
          <textarea :maxlength="120"
                    v-model="editForm.description" style="  width: 80%;height: 150px;border-color: lightgrey;border-radius: 5px"
                    class="texti" placeholder="请编写博文描述(必填)"
          ></textarea>
        </el-form-item>
        <el-form-item label="分类专栏" prop="channel_id" :rules="{ required: true, message: '分类专栏不能为空', trigger: 'blur'}">

          <el-select v-model="editForm.channel_id" placeholder="请选择频道">
            <el-option v-for="(item,index) in baseChannels" :key="item.index" :label="item.channelName" :value="item.id + ''"></el-option>
          </el-select>

        </el-form-item>
        <el-form-item label="博客封面" prop="first_picture" >
          <el-input v-model="editForm.first_picture" placeholder="请输入封面URL"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取消</el-button>
        <el-button type="primary" @click="submitBlog('editForm2')">确定</el-button>
      </div>
    </el-dialog>




  </div>
</template>
<script>
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
import axios from "axios";

export default {
  name: "Writeboke",
  data() {
    return {
      editForm: {  //博客文章表单

        title: '',
        description: '',
        first_picture: '',
        content: '',
        channel_id: '',
        flag:'',
        published: null,

      },

      okimg: 1,

      baseChannels: [],  // 进入页面的时候需要被加载的玩意儿
      success: null, //交互状态!



      editForm2: {  //用来校验的表单

        channel_id: null,
        flag:'',
        published: null,
      },
      oldtags:'',  //字符串类型的标签
      type:{  //分类专栏
        name:''
      },

      dialogFormVisible: false,  //控制发布博客对话框
      dialog2: false,  //控制新增分类专栏对话框


    }
  },
  mounted() {
    //获取频道,本来是打算直接在前端获取的,但是想了想还是算了吧,从服务器这边拿数据吧
    this.axios({
      url: "/boot/getbasechannels",
      method: 'post',
    }).then(res=>{
      this.baseChannels = res.data.baseChannels
      this.success = res.data.success
      if(this.success == '0'){
        alert("数据加载异常")
      }
    })

  },
  methods: {
    //去发布文章

    isokPic(){
      this.axios({
        url:this.editForm.first_picture,

      }).catch(error=>{
          this.okimg = 0
      })
    },

    toSubmit() {
      this.dialogFormVisible = true
      this.initType()
    },
    //初始化文章专栏
    initType(){

    },

    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.addNewType()
        } else {
          return false;
        }
      });
    },
    //校验博客基本内容表单
    tosubmitForm(formName) {
      if(this.editForm.title == ''){
        alert("文章标题不能为空")
        return
      }
      if(this.editForm.content == ''){
        alert("文章内容不能为空")
        return
      }

      this.toSubmit()

    },
    //校验发布博客表单,校验成功后发布博客
    submitBlog(formName) {
      if(this.editForm.description == ''){
        alert("文章描述不能为空")
        return
      }
      if(this.editForm.channel_id == ''){
        alert("请选择分类")
        return;
      }

      this.isokPic()

      console.log(this.okimg)
      setTimeout(this.sendBlog,2000)

    },

    sendBlog(){
      if(this.okimg == 1){

            //发布博客
            this.editForm.content = this.html;

            this.axios({
              url: "/boot/userblog/saveblog",
              method: "post",
              data: {
                boke: this.editForm
              },
              headers:{

                "token": localStorage.getExpire("tokenhole"),
              }
            }).then(res=>{
              if(res.data.success == 0){
                this.dialogFormVisible = false
                alert("博客发送失败")
                return
              }
              alert("博客发布成功")
              this.dialogFormVisible = false

            })

            console.log("博客发布")

          } else {
            return false;
          }


      if(this.okimg == 0){
        alert("目标图片地址拒绝了我们的访问,请更换图片源!")
        return
      }

    },

    imgAdd(pos, $file){
      let param = new FormData()
      param.append("file",$file)
      this.axios({
        url: "/boot/boke/bokeImg",
        method: "post",
        data: param,
        headers:{
          'Content-Type': 'multipart/form-data',
          "token": localStorage.getExpire("tokenhole"),
        }
      }).then(res=>{
        if(res.data.success == 0){
          alert("图片上传失败")
          return
        }
        let url = "/boot"+ res.data.bokeImg
        this.$refs.md.$img2Url(pos,url)

      })

    },


    // 所有操作都会被解析重新渲染
    change(value, render){        //value为编辑器中的实际内容,即markdown的内容,render为被解析成的html的内容
      this.html = render;
    },
    // 提交
    submit(){                    //点击提交后既可以获取html内容,又可以获得markdown的内容,之后存入到服务端就可以了
      console.log(this.editForm.content);
      console.log(this.html);
    },

  }
}
</script>

<style>
.m_container{
  margin-top: 20px;
}
.el-tag + .el-tag {
  margin-left: 10px;
}
.button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}
.input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}



input {

  width: 85%;
  height: 30px;
  border-width: 2px;
  border-radius: 5px;
  border-color: #00c4ff;
  border-bottom-color: #2C7EEA;
  color: #586e75;
  font-size: 15px;

}

#submit {
  width: 10%;
  height: 35px;
  border-width: 0px;
  margin-left: 3%;
  border-radius: 10px;
  background: #1E90FF;
  cursor: pointer;
  outline: none;
  color: white;
  font-size: 17px;
}
#submit:hover {
  background-color: #1E90FF;
  box-shadow: 0 4px 0 powderblue;
}

.texti:focus{
  border-color: #1e88e1;
  outline: 0;
  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
  box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
}
textarea {
  resize: none;

}
</style>


可以看到我这里是故意沉睡了2秒,主要一方面是为了让那个去验证一下图片是否可以被访问,另一方面是故意让用户稍等的,防止爬虫,后面我会再搞个等待加载的玩意。

多表查询分页

那么开始进入正题,前面我介绍了我们整个表的结构,所以这里就不复述了。
需要使用到多表的地方就是那个 频道表和那个博客表,整个也就是整体所在了。
这里提供两个方法

使用注解

整个简单,我们直接在那个mapper文件里面写注解就好了。
在这里插入图片描述

@Mapper
public interface BaseChannelMapper extends BaseMapper<BaseChannel> {
    @Select("select * from userblogs as blog inner join blogandbasechannel as channel on channel.ChannelId=#{id}")
    IPage<UserBlogs> GetChannelBlogs(@Param("page") IPage<UserBlogs> page, @Param("id") int id);

}

使用xml配置文件

这里需要注意几个点。
首先是制定 你的 xml 放置目录,让myabtisplus 扫描到。
我这里放在
在这里插入图片描述
所以修改配置文件

mybatis-plus:
  global-config:

    db-config:
      id-type: auto
      table-underline: false
  mapper-locations:
    - classpath*:/mapping/*.xml
  configuration:
    map-underscore-to-camel-case: false

之后愉快的去写那个xml了

<?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.huterox.whiteholeboot.Entity.Mapper.BaseMapper.BaseChannelMapper">

    <!--sql-->
    <select id="GetChannelBlogs" resultType="com.huterox.whiteholeboot.Entity.Pojo.BokePojo.UserBlogs">
        select * from userblogs as blog inner join blogandbasechannel as channel on channel.ChannelId=#{id} and blog.Id = channel.UserBlogsId
    </select>



</mapper>

分页器

之后是分页器,单表的这个好说,主要是咱们自己定义的方法的分页器,这个很重要。
这个其实也很简单,但是注意细节。
ok 在此之前,不管是单表还是多表,咱们都得去开启咱们的配置

@Configuration
@EnableTransactionManagement
public class MyBatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        PaginationInterceptor page = new PaginationInterceptor();
        page.setDialectType("mysql");
        return page;
    }
}

之后在咱们的那个 mapper里面必须这样写

@Mapper
public interface BaseChannelMapper extends BaseMapper<BaseChannel> {
    @Select("select * from userblogs as blog inner join blogandbasechannel as channel on channel.ChannelId=#{id}")
    IPage<UserBlogs> GetChannelBlogs(@Param("page") IPage<UserBlogs> page, @Param("id") int id);

}

注意返回的类型,切记。我的是 mybatisplus 3.x 先前百度了一些方法,最后发现好像只有这个方法跑通了,至于还有没有其他的方式我也不太清楚,返回类型必须是 IPage 别的不行,至少我这里是这样的。

然后就是使用嘛。

    public Page GetChannelBlogs(int channel_id,int page,int size){
        Page<UserBlogs> page2 = new Page<>(page,size);
        baseChannelMapper.GetChannelBlogs(page2,channel_id);
        return page2;
    }

之后就是其他的查询,都是很简单的。

博客显示

这个主要还是前端的问题。
这里说两个

显示图片尺寸

由于我这边的话是把html文件放到我们的数据库里面了,所以我们可以直接加载,但是是放在 v-html
里面的,所以我们要想修改里面的样式就必须这样(假设我修改图片样式,原来的太丑了)

 /deep/ img{
    width: 70%;
    height: 400px;
    margin-left: 15%;
   margin-top: 2%;
}

加上 /deep/

代码高亮

这个主要是实现这个效果
在这里插入图片描述
这个其实也简单,我搞了小半天。
导入这个玩意



import 'highlight.js/styles/googlecode.css'

最后来看看整体代码吧

<template>
	<div style="background-color: #f8f8e7">
		<el-row class="main" type="flex" justify="center">
			<el-col :span="16">


        <div style="margin: 0 auto">
          <h3>{{title}}</h3>
        </div>
        <br>
        <h5>
          作者:{{author}}
        </h5>

        <h4>
          <i class="el-icon-time"></i>: {{time}}
        </h4>
        <br>
        <p> 原文: </p>
        <div class="markdown-body" style="border-radius: 5px;border: solid 2px #47aeef" v-model="body" v-html="body"></div>






			</el-col>
		</el-row>
	</div>
</template>

<script>

import 'mavon-editor/dist/css/index.css'

import 'highlight.js/styles/googlecode.css'


	export default {
		name: 'article',



    data(){
      return{

        author :this.$route.params.autoer,
        blogsId: this.$route.params.blogsId,
        title: this.$route.params.title,
        time: this.$route.params.time,
        body: null

      }
    },
    mounted() {

      this.axios({
        url: "/boot/base/viewblog",
        params:{
          BlogId:this.blogsId
        }
      }).then(res=>{
        this.body = res.data.body.body
        if(res.data.success == "0"){
          alert("博客加载异常,请重试~")
        }
      })
    }
  }
</script>

<style scoped>
	#artcle-info {
		padding: 20px;
		background-image: url(../assets/vue.jpg);
		margin-bottom: 40px;
	}

	#artcle-info .abstract {
		color: #ffffff;
		border-left: 3px solid #F56C6C;
		padding: 10px;
		background-color: rgba(126, 129, 135, 0.3);
	}

	#artcle-info .timeAndView {
		padding: 20px;
		line-height: 30px;
		font-size: 16px;
		color: #ffffff;
	}

	pre.has {
		color: #ffffff;
		background-color: rgba(0, 0, 0, 0.8);
	}

	img.has {
		width: 100%;
	}

	#statement {
		border-left: 3px solid #F56C6C;
		padding: 20px;
		background-color: #EBEEF5;
	}

 /deep/ img{
    width: 70%;
    height: 400px;
    margin-left: 15%;
   margin-top: 2%;
}

</style>

总结

到目前为止的话,最基础的功能做好了,勉强能用来,但是目前还是没有时间,所以先到这里,并且我现在还缺少后台管理 的前端代码所以没法继续做下去了(这里如果你有现成的后台管理模板最好是vue 2x的话,如果可以的话,分享分享呗~~)。目前的效果的话大概是这个样子的(后面再整理整理推送github gitee):

首页
在这里插入图片描述
在这里插入图片描述

文章页
在这里插入图片描述

写博客页
在这里插入图片描述
频道页面
在这里插入图片描述
进入频道
在这里插入图片描述

个人中心
在这里插入图片描述
在这里插入图片描述
趣味工具
在这里插入图片描述
交换友链
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Huterox

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值