所用技术
- 数据库:mysql
- 后台框架:springboot,mybatis plus
- 前台框架:Vue
- 实体类:lombok
- 异步:axios
一丶微博留言后端
小贴士:约定>配置>编码
先做好准备,在去写代码
1.sql语句
CREATE TABLE `ol_msg` (
`id` VARCHAR(200) PRIMARY KEY ,
`create_date` DATETIME DEFAULT NULL,
`content` VARCHAR(2000) DEFAULT NULL,
`acc` INT(8) DEFAULT NULL,
`ref` INT(8) DEFAULT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8
2.改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chufeng</groupId>
<artifactId>springboot-vue-blog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-vue-blog</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.1</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>com.chufeng.springbootvueblog.SpringbootVueBlogApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.写yml文件
server:
port: 8080
spring:
application:
name: springboot-vueblog
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
name: defaultDataSource
password: 123456
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
4.业务类
BlogMapper
package com.chufeng.springbootvueblog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chufeng.springbootvueblog.entities.Blog;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BlogMapper extends BaseMapper<Blog> {
}
BlogService接口
package com.chufeng.springbootvueblog.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chufeng.springbootvueblog.entities.Blog;
public interface BlogService extends IService<Blog> {
}
BlogServiceImpl实现类
package com.chufeng.springbootvueblog.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chufeng.springbootvueblog.entities.Blog;
import com.chufeng.springbootvueblog.mapper.BlogMapper;
import com.chufeng.springbootvueblog.service.BlogService;
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService {
}
5.配置类
MybatisPlusConfig 数据分页配置类
package com.chufeng.springbootvueblog.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
@Configuration
public class MyBatisPlusConfig {
// 最新版分页
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
AppConfig跨域配置类
package com.chufeng.springbootvueblog.config;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class AppConfig {
// 全局跨域配置: 可以在java后台配置,也可以在vue前台配置
private CorsConfiguration addCorsConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
//请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", addCorsConfig());
return new CorsFilter(source);
}
}
BlogController
package com.chufeng.springbootvueblog.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chufeng.springbootvueblog.entities.Blog;
import com.chufeng.springbootvueblog.service.BlogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@Slf4j
@RestController
@RequestMapping("/api/blogs")
public class BlogController {
@Resource
private BlogService blogService;
@GetMapping
public IPage<Blog> selectByPage(Integer pageNo, Integer pageSize){
return blogService.page(new Page<>(pageNo,pageSize));
}
@PostMapping
public boolean savaBlog(@RequestBody Blog blog){
//打印日志
log.info(blog.toString());
return blogService.save(blog);
}
@DeleteMapping("/{id}")
public boolean deleteBlogById(@PathVariable("id") String id ){
return blogService.removeById(id);
}
@PutMapping
public boolean updateBlog(@RequestBody Blog blog){
return blogService.updateById(blog);
}
}
二丶Vue前端
需要导入
- bootstrap.css
- axios.js
- jquery.js
- vue.js
自定义css代码
@charset "utf-8";
*{margin:0; padding:0;}
li{list-style:none;}
img{border:none;}
a{text-decoration:none;}
input,textarea{outline:none; resize:none; border:none;}
.clearfix:after{visibility:hidden;display:block;font-size:0;content:" ";clear:both;height:0;}
.clearfix{*zoom:1;}
body{background:url(../img/bg.jpg) center center no-repeat; background-size:100% 140%;}
/*登陆*/
.loginBox{
width:278px; padding:30px 80px 20px 80px; margin:40px auto;
background-image: -webkit-linear-gradient(top,rgb(255,255,255),rgb(242,242,242));
box-shadow: 0 0 3px rgba(21,62,78,0.8); position:relative;
border-radius:2px;
}
.loginList{width:100%;}
.loginList li{margin-bottom:10px; overflow:hidden;}
.hTxt{font:18px/1.5 "Microsoft YaHei";}
.inputs{width:260px; display:block; font-weight:bold; background:url(../img/inputBg.png) 0 0 no-repeat; height:14px; line-height:14px; padding:9px; color:#666;}
.loginList .btns{text-align:right; background:none; width:280px;}
.reg{background:url(../img/btns.png) 0 -42px no-repeat; width:70px; height:42px; display:inline-block; overflow:hidden; opacity:.8;}
.login{background:url(../img/btns.png) 0 0 no-repeat; width:70px; height:42px; display:inline-block; overflow:hidden; opacity:.8;}
.reg:hover,.login:hover{opacity:1;}
.reg:active,.login:active{opacity:.9;}
.look{text-align:right; font:12px/1.2 "Microsoft YaHei"; color:#999;}
/*ad*/
.ad{background:url(../img/ad.png) left top no-repeat; position:fixed; bottom:20px; height:16px; left:50%; width:106px; margin-left:-53px;}
::selection {background-color:#669900; color:#ffffff;}
::-moz-selection {background-color:#669900; color:#ffffff;}
weibo.css代码
@charset "utf-8";body,ul,ol,li,dl,dt,dd,p,h1,h2,h3,h4,h5,h6,form,fieldset,table,td,img,div{margin:0;padding:0;border:0}
body{font-size:12px;font-family:"Microsoft YaHei"}
ul,ol{list-style-type:none}
select,input,img,select{vertical-align:middle}
a{text-decoration:underline;color:#313030}
a{blr:expression(this.onFocus=this.blur())}
input,textarea{outline:0;resize:none}
a{outline:0}
.msgArea{width:755px;overflow:hidden;margin:0 auto;font-family:"Microsoft YaHei"}
.commentOn{width:753px;display:block;overflow:hidden;border:#a5bcff solid 1px;background:#f3f8fd;margin-top:25px;font-family:Verdana}
.reply{overflow:hidden;padding:10px 20px;background:#FFF;border-top:#e9e9e9 solid 1px;border-bottom:#e9e9e9 solid 1px}
.userInfo{display:block;overflow:hidden;height:25px;border-bottom:#bababa solid 1px}
.userName{float:left;background:url(../img/userBj.png) left center no-repeat;padding-left:15px;color:#000;font-size:14px;font-weight:bold}
.replyTime{float:left;color:#8b8585;line-height:30px;font-size:11px}
.replyContent{line-height:24px;font-size:14px;color:#2b2b2b;font-family:"Microsoft YaHei"}
.operation{clear:both;width:100%;height:30px;margin-top:8px}
.handle{float:right;padding-top:6px}
.handle a{text-decoration:none;float:left;margin-left:12px;background:url(../img/icons.png) 0 0 no-repeat;height:18px;line-height:18px;padding-left:20px}
.handle .top_icon{background-position:0 0}
.handle .down_icon{background-position:0 -17px}
.handle .cut{background-position:0 -33px}
.handle a:active{color:#09F}
.noContent{text-align:center;display:block;background:#FFF;font:14px/2.3 "Microsoft YaHei";border-bottom:#e9e9e9 solid 1px;border-top:#e9e9e9 solid 1px;color:#999}
.takeComment{width:713px;display:block;overflow:hidden;border:#a5bcff solid 1px;background:#f3f8fd;margin-top:25px;font-family:Verdana;padding:15px 20px}
.takeTextField{width:701px;height:70px;border:#b1b1b1 solid 1px;clear:both;display:block;margin:10px 0 10px 0;line-height:20px;padding:5px;box-shadow:inset 0 0 5px #DDD;font-family:"Microsoft YaHei"}
.takeSbmComment{display:block;overflow:hidden}
.takeSbmComment span{float:right;color:#CCC;line-height:37px;padding-right:10px}
.inputs{float:right;width:125px;height:37px;border:none 0;background:tranparent;background:url(../img/takeSbmComment.png) left top no-repeat;cursor:pointer;opacity:.8}
.inputs:hover{opacity:1}
.inputs:active{opacity:.9}
.messList{overflow:hidden}
.page{text-align:right;font-size:0;font-family:Verdana;padding:10px 16px}
.page a{display:inline-block;height:20px;padding:0 7px;border:#CCC solid 1px;font:12px/20px Verdana;text-decoration:none;margin-left:5px;background:#FFF}
.page a:hover{background:#09F;color:#FFF;border-color:#09F}
.page .active{background:#CCC}
.page a:active{opacity:.8}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--公共样式-->
<link href="css/common.css" rel="stylesheet" />
<!--微博样式-->
<link href="css/weibo.css" rel="stylesheet" />
<!--bootstrap-->
<link href="css/bootstrap.css" rel="stylesheet" />
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<title>微博</title>
</head>
<body>
<div id="app" class="container">
<!--留言-->
<div class="takeComment">
<!--回复内容-->
<textarea name="textarea" class="takeTextField" id="tijiaoText" v-model="content" v-on:keyup.enter="addBlog()"></textarea>
<div class="takeSbmComment">
<!--回复按钮-->
<button class="btn btn-default" @click.stop="addBlog()">回复</button>
<span>(可按 Enter 回复)</span>
</div>
</div>
<!--已留-->
<div class="commentOn">
<!--如果没有数据,显示暂无留言-->
<div class="noContent">暂无留言</div>
<!--如果查询有数据,使用列表显示-->
<div class="messList">
<div class="reply" v-for="(item,index) in records">
<p class="replyContent" v-cloak>{{item.content}}</p>
<p class="operation">
<span class="replyTime" v-cloak>{{item.createDate}}</span>
<span class="handle">
{{userid}}{{useracc}}{{userref}}
<button class="top" @click="updateAcc(item)">{{item.acc}}</button>
<button class="down_icon"@click="updateRef(item)" >{{item.ref}}</button>
<button @click="delMsg(item.id)">删除</button>
</span>
</p>
</div>
</div>
<p>
总数据:{{total}}
<a>下一页</a>
</p>
</div>
</div>
<script>
//创建一个axios的实例
let http=axios.create(({//基础配置
baseURL: 'http://127.0.0.1:8081/api/',
timeout:1000
}))
let _this;
let vm=new Vue({
el:"#app",
data:{
content:'',//添加评论的内容
records:[],//数组数组
pageNo:1,//默认页码
pageSize:2,//默认页大小
total:0,//总数据,
userid:1,
useracc:0,
userref:0
},
mounted(){//挂载
_this=this;
this.initData(this.pageNo,this.pageSize);
},
methods:{
initData:function (pageNo,pageSize){
http.get("/blogs?pageNo="+pageNo+"&pageSize="+pageSize+"")
.then(function (resp){
//注意取值的时候要使用resp.data
_this.records=resp.data.records;
_this.total=resp.data.total;
console.log("相应数据"+resp);
})//成功回调
.catch(function (error){
console.log(error)
})
},
addBlog:function (){
let data={
"content":this.content,
"acc":10,
"ref":0
}
http.post("/blogs",data)
.then(function (resp){
console.log(resp);
//清空内容
_this.content="";
_this.initData(_this.pageNo,_this.pageSize);
}).catch(function (error){
console.log(error)
})
},// 删除
delMsg:function(id){
http.delete("/blogs/"+id)
.then(function(resp){
console.log(resp);
// 重新加载数据
_this.initData(_this.pageNo,_this.pageSize);
})
.catch(function(error){
console.log(error)
})
},//修改
updateAcc:function(data) {
data.acc=data.acc+1;
console.log(data)
http.put("/blogs",data)
.then(function (resp) {
console.log(resp);
// 重新加载数据
_this.initData(_this.pageNo,_this.pageSize);
})
.catch(function (error) {
console.log(error)
})
},
updateRef:function(data) {
data.ref=data.ref+1;
console.log(data)
http.put("/blogs",data)
.then(function (resp) {
console.log(resp);
// 重新加载数据
_this.initData(_this.pageNo,_this.pageSize);
})
.catch(function (error) {
console.log(error)
})
}
}
});
</script>
</body>
</html>
最终效果