Springboot+Vue实现发表文章功能

点击上方[全栈开发者社区]右上角[...][设为星标⭐]

效果图

前端编辑页面


文章列表页面

文章详情页面

环境介绍

JDK:1.8

数据库:Mysql5.6

前端:Vue

后端:SpringBoot

核心代码介绍

AtricleCtrle.class

@RestController
@RequestMapping("/article")
@CrossOrigin
public class ArticleCtrler 
{
  @Autowired
  private ArticleService articleService;
  @ApiOperation(value="添加文章")
  @PostMapping("/addarticle")
  public Object addarticle(@Valid Article vo)
  {
    try 
    {
      articleService.insert(vo); 
      return Result.success(null);
    }
    catch (Exception e) 
    {
      e.printStackTrace();
    }
     return Result.error(CodeMsg.SERVER_ERROR);
  }
  @PostMapping("/loadPage")
  @ApiOperation(value="文章分页列表")
  public Object loadPage(@Valid Article_Condit vo)
  {
    try 
    {
      PageInfo<Article> loadPage = articleService.loadPage(vo);
      return Result.success(loadPage);
    }
    catch (Exception e) 
    {
      e.printStackTrace();
    }
     return Result.error(CodeMsg.SERVER_ERROR);
  }
}

ArticleService.interface

public interface ArticleService 
{
  void insert(Article vo);
  PageInfo<Article> loadPage(Article_Condit vo);
}

ArticleServiceImpl.class

@Service
@Transactional
public class ArticleServiceImpl implements ArticleService 
{
  @Autowired
  private ArticleDao articleDao;
  @Override
  public void insert(Article vo) 
{
    vo.setCreatedatetime(new Timestamp(new Date().getTime()));
    vo.setCreateuserid("system");
    articleDao.save(vo);
  }
  @Override
  public PageInfo<Article> loadPage(Article_Condit vo) 
{
    PageHelper.startPage(vo.getPageIndex()==null?0:vo.getPageIndex(), vo.getPageSize()==null?0:vo.getPageSize());
    List<Article> findByCondit = articleDao.findByCondit(vo);
    return new PageInfo<Article>(findByCondit);
  }
}

ArticleDao.class

@Repository
public interface ArticleDao 
{
  void save(Article vo);
  List<Article> findByCondit(Article_Condit vo);
}


Helloworld.vue

@ApiModel(value="发布文章", description="发布文章")
public class Article implements Serializable
{
  private static final long serialVersionUID = 1L;
  @ApiModelProperty(value = "文章编号",hidden = true)
  private Long id;
  @ApiModelProperty(value = "文章标题",required = true)
  @NotBlank(message = "标题不能为空")
  private String title;
  @ApiModelProperty(value = "文章描述",required = true)
  @NotBlank(message = "文章描述不能为空")
  private String description;
  @ApiModelProperty(value = "文章内容",required = true)
  @NotBlank(message = "文章内容不能为空")
  private String content;
  @ApiModelProperty(value = "文章类型",required = false)
  private String articletype;
  @ApiModelProperty(value = "创建时间",hidden = true)
  @JsonFormat(pattern="yyyy-MM-dd HH:mm", timezone="GMT+8")
  private Timestamp createdatetime;
  @ApiModelProperty(value = "创建人",hidden = true)
  private String createuserid;
  public Long getId() {
    return id;
  }
  public void setId(Long id) {
    this.id = id;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    this.description = description;
  }
  public String getContent() {
    return content;
  }
  public void setContent(String content) {
    this.content = content;
  }
  public Timestamp getCreatedatetime() {
    return createdatetime;
  }
  public void setCreatedatetime(Timestamp createdatetime) {
    this.createdatetime = createdatetime;
  }
  public String getCreateuserid() {
    return createuserid;
  }
  public void setCreateuserid(String createuserid) {
    this.createuserid = createuserid;
  }
  public String getArticletype() {
    return articletype;
  }
  public void setArticletype(String articletype) {
    this.articletype = articletype;
  }
  @Override
  public String toString() {
    return "Article [id=" + id + ", title=" + title + ", description=" + description + ", content=" + content
        + ", articletype=" + articletype + ", createdatetime=" + createdatetime + ", createuserid="
        + createuserid + "]";
  }
}

articleMapper.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.yxyz.dao.ArticleDao">
  <insert id="save" parameterType="com.yxyz.vo.Article">
    insert 
      into t_article(title,description,content,articletype,createdatetime,createuserid)
    values
    (
      #{title,jdbcType=VARCHAR},#{description,jdbcType=VARCHAR},#{content,jdbcType=LONGVARCHAR},
      #{articletype,jdbcType=VARCHAR},#{createdatetime,jdbcType=TIMESTAMP},#{createuserid,jdbcType=VARCHAR}
    )
  </insert>
  <select id="findByCondit" parameterType="com.yxyz.condit.Article_Condit" resultType="com.yxyz.vo.Article">
    select t1.* from t_article t1 where 1=1
    <if test="articletype != null and articletype !=''">
      and t1.articletype like concat('%',#{articletype},'%')
    </if>
    <if test="createuserid != null and createuserid !=''">
      and t1.createuserid = #{createuserid}
    </if>
  </select>
</mapper>

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'


Vue.use(ElementUI)
Vue.config.productionTip = false


new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app')

About.vue

<template>
  <div class>
    <h3>编辑页面</h3>
    <el-form ref="form" :model="form" label-width="80px">
      <el-form-item label="标题">
        <el-input v-model="form.title"></el-input>
      </el-form-item>
      <el-form-item label="描述">
        <el-input v-model="form.description"></el-input>
      </el-form-item>
      <el-form-item label="正文">
        <quill-editor
          ref="myQuillEditor"
          class="editor"
          v-model="form.content"
          :options="editorOption"
          @blur="onEditorBlur($event)"
          @focus="onEditorFocus($event)"
          @ready="onEditorReady($event)"
        />
      </el-form-item>
      <el-button class="btn" block type="primary" @click="submit">提交</el-button>
    </el-form>
  </div>
</template>


<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'


import { quillEditor } from 'vue-quill-editor'
import axios from 'axios'
export default {
  components: { quillEditor },
  props: {},
  data() {
    return {
      form: {
        title: '',
        description: '',
        content: '',
      },
      editorOption: {
        modules: {
          toolbar: [
            ['bold', 'italic', 'underline', 'strike'], //加粗,斜体,下划线,删除线
            ['blockquote', 'code-block'], //引用,代码块


            [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
            [{ list: 'ordered' }, { list: 'bullet' }], //列表
            [{ script: 'sub' }, { script: 'super' }], // 上下标
            [{ indent: '-1' }, { indent: '+1' }], // 缩进
            [{ direction: 'rtl' }], // 文本方向


            [{ size: ['small', false, 'large', 'huge'] }], // 字体大小
            [{ header: [1, 2, 3, 4, 5, 6, false] }], //几级标题


            [{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
            [{ font: [] }], //字体
            [{ align: [] }], //对齐方式


            ['clean'], //清除字体样式
            // ['image', 'video'], //上传图片、上传视频
          ],
        },
        theme: 'snow',
      },
    }
  },
  computed: {},
  created() {},
  mounted() {},
  watch: {},
  methods: {
    onEditorBlur(quill) {
      console.log('editor blur!', quill)
    },
    onEditorFocus(quill) {
      console.log('editor focus!', quill)
    },
    onEditorReady(quill) {
      console.log('editor ready!', quill)
    },
    submit() {
      if (!this.form.title) {
        this.$message('请输入标题')
      }
      if (!this.form.description) {
        this.$message('请输入描述')
      }
      if (!this.form.content) {
        this.$message('请输入正文')
      }


      let formData = new FormData()
      formData.append('title', this.form.title)
      formData.append('description', this.form.description)
      formData.append('content', this.form.content)
      // 发送 POST 请求
      axios({
        method: 'post',
        url: 'http://139.159.147.237:8080/yxyz/article/addarticle',
        data: formData,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      })
        .then(function(response) {
          if (res.code === 0) {
            this.$message('提交成功')
          }
          // this.form = {
          //   title: '',
          //   description: '',
          //   content: '',
          // }
          this.$router.goBack()
        })
        .catch(function(error) {
          console.log(error)
        })
    },
  },
}
</script>


<style scoped lang="less">
.editor {
  height: 500px;
}
.btn {
  margin-top: 100px;
}
</style>


Detail.vue

<template>
  <div class="">
    <h2>文章详情</h2>
    <el-card>
      <div class="title">{{ detail.title }}</div>
      <div class="des">{{ detail.description }}</div>
      <div class="con" v-html="detail.content"></div>
      <div class="time">{{ detail.createdatetime }}</div>
    </el-card>
  </div>
</template>


<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      detail: {},
    }
  },
  computed: {},
  created() {
    console.log(this.$route)
    let item = this.$route.params.item


    this.detail = item
  },
  mounted() {},
  watch: {},
  methods: {},
}
</script>


<style scoped lang="less">
.title {
  font-weight: bold;
  font-size: 16px;
  text-align: left;
  margin-bottom: 10px;
}
.des {
  font-size: 14px;
  text-align: left;
  margin-bottom: 10px;
}
.con {
  font-size: 14px;
  text-align: left;
  margin-bottom: 10px;
}
.time {
  font-size: 14px;
  text-align: left;
}
</style>


Home.vue

<template>
  <div class="">
    <h2>文章详情</h2>
    <el-card>
      <div class="title">{{ detail.title }}</div>
      <div class="des">{{ detail.description }}</div>
      <div class="con" v-html="detail.content"></div>
      <div class="time">{{ detail.createdatetime }}</div>
    </el-card>
  </div>
</template>


<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      detail: {},
    }
  },
  computed: {},
  created() {
    console.log(this.$route)
    let item = this.$route.params.item


    this.detail = item
  },
  mounted() {},
  watch: {},
  methods: {},
}
</script>


<style scoped lang="less">
.title {
  font-weight: bold;
  font-size: 16px;
  text-align: left;
  margin-bottom: 10px;
}
.des {
  font-size: 14px;
  text-align: left;
  margin-bottom: 10px;
}
.con {
  font-size: 14px;
  text-align: left;
  margin-bottom: 10px;
}
.time {
  font-size: 14px;
  text-align: left;
}
</style>


application.yml

spring:
  profiles:
    active: dev

application-dev.yml

server:
  port: 8080
  servlet:
    context-path: /yxyz
spring:
  datasource:
    name: yxyz
    url: jdbc:mysql://localhost/test?serverTimezone=GMT%2b8&characterEncoding=UTF8
    username: root
    password: 123
 # 使用druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20
## 该配置节点为独立的节点
mybatis:
  mapper-locations: classpath:mapping/*Mapper.xml #注意:一定要对应mapper映射xml文件的所在路径
  type-aliases-package: com.yxyz.vo # 注意:对应实体类的路径
#打印sql最终填充的参数值
  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


觉得本文对你有帮助?请分享给更多人

关注「全栈开发者社区」加星标,提升全栈技能
本公众号会不定期给大家发福利,包括送书、学习资源等,敬请期待吧!
如果感觉推送内容不错,不妨右下角点个在看转发朋友圈或收藏,感谢支持。
好文章,留言、点赞、在看和分享一条龙吧❤️
  • 6
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
* 前台采用的是拟态风格,支持黑白主题切换。 * 后台用的是[Naive UI](https://www.naiveui.com/),有暗黑及亮白两种主题,支持全局设置修改界面样式。 * 文章编辑采用Markdown,语法简洁。 * 评论加入了动态表情包,体验更加有趣。 * 图片懒加载显示,避免网路不畅导致图片加载时空白。 * 留言采用贴纸效果,可选择留言主题。 * 前后端分离部署。 * 用户申请友链,后台可控,用户可修改。 * 动态修改文章预览样式。 * 支持QQ登录和邮箱绑定,绑定后可互相登录。 * 支持发表说说,分享日常生活。 * 采用JWT进行用户授权,降低服务器查询数据库的次数。 * 支持搜索结果高亮。 * 上传图片(oss或本地)。 * 基于RBAC模型,动态修改用户权限,根据权限限制操作。 * AOP注解实现日志管理。 * 后台可修改轮播背景图片,博客配置等信息。 * 后端多模块设计,方便后期升级维护。 * 日志拆分记录,快速定位问题。 * 支持https配置 * 即时聊天室 ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
系统采用前后端分离的架构。 前端使用:vue2 + element–ui + axios 后端使用:Spring Boot + MyBatis plus + shrio + Redis + jwt + hibernate validater 功能简述如下: 大体分为用户模块、文章模块、搜索模块和交互模块。 2.1 用户模块 包括基本的用户登录注册功能、用户个人信息的修改维护功能。 登陆后用户的个人主界面可以按时间顺序查看到自己历史发布的博客记录。 2.2 文章模块 包括文章的编辑发布文章的修改、文章分类标签的设计、文章的删除功能 文章分为标题、摘要、标签、主体内容四部分。 使用 Markdown 格式的一个插件,使得用户可以编辑博客主体的内容。 文章发布后,若是文章的拥有者可以随时修改文章的内容,并且可以设置文章是否可以为其他用户所见(即可见性),也可以随时删除文章。 2.3 交互模块 包括点赞、收藏、发表评论、查看他人文章功能。 用户点赞和收藏过的文章会按时间顺序分别放入到用户个人主页下的我的点赞和我的收藏栏目。 所有的用户都可以在文章下面发表评论,并可无限制地对评论进行再评论。显示用户的评论时间和评论的用户名。 2.4 搜索模块 用户可在博客大厅里的搜索栏里按照用户或者博客的分类,来搜索用户或者博客。 或者直接使用一旁的标签分类直接搜索对应分类的博客。 搜索功能会将所有包含用户搜索的关键字的用户或者博客搜索出来,供用户查看。
基于SpringBoot+Vue的在线BLOG网是一个集成了前端和后端技术的在线BLOG平台,主要用于用户发表、分享和交流个人博客文章。该系统的源码、部署说明和系统介绍已经打包成一个zip文件,方便用户使用。 该系统的前端部分采用了Vue框架进行开发,主要实现了用户界面和交互,包括登录、注册、文章列表、文章详情、评论交流等功能。而后端部分则采用了SpringBoot框架,负责处理用户请求、管理用户数据并提供相应的API接口。 该系统具有良好的用户体验,用户可以灵活地选择文章、进行发表、搜索和评论等。同时,该系统也支持用户管理自己的博客文章、收藏和分享其他用户的博客文章,并提供了实时在线交流功能。同时,该系统还支持对用户权限的管理,可以对不同用户进行分组和授权,保证信息的安全性。 对于博客作者而言,该系统可以方便快捷地发表自己的博客文章、管理自己的文章信息和进行评论交流等。对于其他用户而言,该系统可以方便快捷地搜索和阅读博客文章、评论和分享其他用户的博客文章等,提高了博客交流的效率和实用性。同时,对于开发者而言也是一个学习VueSpringBoot技术的不错案例,值得一试。 总之,基于SpringBoot+Vue的在线BLOG网是一个功能齐全、易用、实用性很高的在线博客平台,能够方便地进行博客文章的发表、管理和交流,提高了博客交流的效率和实用性,同时,对于开发者而言也是一个学习VueSpringBoot技术的不错案例,值得一试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值