目录
前言
这个网页是一个社团管理网页,是一个毕设,设计的技术比较简单,写这个博客主要是一个回忆整理的过程,如果有更好的实现可以在评论区告知。如果发现过程有所错误,也请不吝斧正。
代码在最后
中间过程只是一些测试的代码
我的后台部分技术也是做这次毕设才第一次接触的,非常感谢一个大佬的指导,大佬在简书上也发表一些技术类文章,感兴趣的可以看一下。此处附上这位大佬的其中一篇文章地址。
搭建Springboot框架
后台开发工具:IDEA(我用的版本是IntelliJ IDEA 2019.2.3 x64)
IDEA配置tomcat
IDEA配置maven
以上两个配置百度一下就有很多教程,可以按照教程走。
完成后如下图:
可以直接删除
新建一个controller文件夹,新建一个HelloWorld.java,测试一下
可以在浏览器输入localhost:8080/hello 就可以看到有Hello World的输出
Springboot整合JDBC
我用的是MySQL数据库,用的Navicat
删除resources文件夹里的 application.properties文件,新增application.yml文件
往里面写入数据库的配置信息
#配置DataSourceSpring
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/你的数据库的名字?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
username: root
password: 你的数据库密码
initialSize: 5
maxActive: 100
minIdle: 3
maxWait: 50000
main:
banner-mode: "off"
测试连接:
在test里写入
@SpringBootTest
class StoryApplicationTests {
@Autowired
private DataSource dataSource;
@Test
public void contextLoads() {
System.out.println("---------------##--------------------");
System.out.println("dataSource类型:"+dataSource.getClass());
System.out.println("connection连接:"+dataSource.getConnection());
}
}
运行,可以看到输出:
Springboot整合MyBatis-plus
添加依赖
可以去这个网址下依赖 https://mvnrepository.com/
mybatis-plus
连接池 druid
在pom.xml文件里写入
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
字段的含义可以参见 mybatis-plus 官方手册(虽然我看了就忘记了,也不知道为什么写这些)https://mp.baomidou.com/guide/
#mybatis-plus
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
typeAliasesPackage: com.claire.bean
global-config:
field-strategy: not_empty
column-underline: true
capital-mode: true
logic-delete-value: 1
logic-not-delete-value: 0
db-type: mysql
refresh: true
cache-enabled: false
使用lombok
pom.xml增加依赖
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
新建一个common文件夹,创建如图六个文件,主要作用是统一规范返回的信息。使得返回的信息不是单一的数据,而是包含状态码的对象。 ClaireConstat是一个产量类。
新建文件夹bean(存放我们的实体类,与数据库表对应)
stories
package com.claire.bean;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author Claire
*/
@TableName("stories")
@Data
public class Stories implements Serializable {
/**
* 故事ID
*/
@TableId(type = IdType.AUTO)
private Integer sId;
/**
* 故事名称
*/
private String sTitle;
/**
* 故事作者
*/
private String sAuthor;
/**
* 故事内容
*/
private String content;
/**
* 故事简介
*/
private String summary;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private LocalDateTime createTime;
}
新增mapper包、service包,还有一个工具类包utils,新增下面的文件
DataUtils
package com.claire.utils;
import org.springframework.util.StringUtils;
/**
* @author Claire
* 判断数据非空等
*/
public class DataUtils {
/**
* 判断字符串非空
* @param originalStr
* @return
*/
public static boolean isNotEmptyStr(String originalStr){
return !isEmptyStr(originalStr);
}
/**
* 判断字符串为空
* @param originalStr
* @return
*/
private static boolean isEmptyStr(String originalStr) {
return StringUtils.isEmpty(originalStr);
}
/**
* 判断对象为null
* @param originalData
* @return
*/
public static boolean isNull(Object originalData){
return originalData==null;
}
/**
* 判断对象不为null
* @param originalData
* @return
*/
public static boolean isNotNull(Object originalData){
return !isNull(originalData);
}
/**
* 判断两个对象是否相等
* @param sourceData
* @param targetData
* @return
*/
public static boolean isEqual(Object sourceData,Object targetData){
return sourceData.equals(targetData);
}
/**
* 判断两个对象是否不等
* @param sourceData
* @param targetData
* @return
*/
public static boolean isNotEqual(Object sourceData,Object targetData){
return !isEqual(sourceData,targetData);
}
/**
* 截取字符串一定长度
* @param str
* @param length
* @return
*/
public static String limitLength(String str,int length){
if(isEmptyStr(str)){
return str;
}
return str.length()>length?str.substring(0,length):str;
}
}
在controller里增加ServiceController,目前的结构是,已经增加了一个获取数据库数据的函数,想尝试的话代码贴在下面
ApiCode
package com.claire.common;
/**
* @author Claire
*/
public interface ApiCode<Enum> {
public int getCode();
public String getCodeMsg();
}
ApiResponse
package com.claire.common;
import lombok.Data;
/**
* @author Claire
*/
@Data
public class ApiResponse<T> implements java.io.Serializable {
/**
* 状态码
*/
private int code;
/**
* 错误消息
*/
private String msg;
/**
* 消息响应的对象,比如列表
*/
private T data;
/**
* 时间戳
*/
private long time;
public ApiResponse(){
super();
}
public ApiResponse(ApiCode apiCode,String msg,T data){
this.code=apiCode.getCode();
this.msg=msg;
this.data=data;
this.time=System.currentTimeMillis();
}
public ApiResponse(ApiCode apiCode,T data){
this.code= apiCode.getCode();
this.msg=apiCode.getCodeMsg();
this.data=data;
this.time=System.currentTimeMillis();
}
public static ApiResponse success(Object data) {
return new ApiResponse(BaseApiCode.SUCCESS, BaseApiCode.SUCCESS.getCodeMsg(), data);
}
public static ApiResponse success() {
return new ApiResponse(BaseApiCode.SUCCESS, BaseApiCode.SUCCESS.getCodeMsg(), "");
}
public static ApiResponse failed(ApiCode code, String msg) {
return new ApiResponse(code, msg, "");
}
public static ApiResponse failed(String msg) {
return new ApiResponse(BaseApiCode.FAILED, msg, "");
}
public static ApiResponse failed() {
return new ApiResponse(BaseApiCode.FAILED, BaseApiCode.FAILED.getCodeMsg(), "");
}
public static ApiResponse failed(Object data) {
return new ApiResponse(BaseApiCode.FAILED, BaseApiCode.FAILED.getCodeMsg(), data);
}
}
BaseApiCode
package com.claire.common;
/**
* @author Claire
*/
public enum BaseApiCode implements ApiCode<Enum>{
SUCCESS(0,"操作成功"),
FAILED(-1,"操作失败"),
SYSTEM_ERROR(500,"System Error"),
VALID_ERROR(-1001,"验证失败"),
OBJECT_NOT_EXISTS(-1003,"对象不存在"),
NEED_LOGIN(401, "需要登录"),
RESOURCE_NOT_FOUND(404, "Not Found");
/**
* 错误码
*/
private int code;
/**
* 错误信息
*/
private String codeMsg;
private BaseApiCode(int code,String codeMsg){
this.code=code;
this.codeMsg=codeMsg;
}
@Override
public int getCode() {
return this.code;
}
@Override
public String getCodeMsg() {
return this.codeMsg;
}
}
BaseApiCodeImp
package com.claire.common;
/**
* @author Claire
*/
public class BaseApiCodeImp implements ApiCode {
private Integer code;
private String codeMsg;
@Override
public int getCode() {
return this.code;
}
@Override
public String getCodeMsg() {
return this.codeMsg;
}
}
BaseController
package com.claire.common;
/**
* @author Claire
*/
public class BaseController {
/**
* 成功返回数据
*
* @return ApiResponse
*/
protected ApiResponse success() {
return ApiResponse.success();
}
/**
* 成功返回数据
*
* @param data 待返回数据
* @return ApiResponse
*/
protected ApiResponse success(Object data) {
return ApiResponse.success(data);
}
/**
* 成功返回消息
*
* @param message 待返回成功消息
* @return ApiResponse
*/
protected ApiResponse success(String message) {
return new ApiResponse(BaseApiCode.SUCCESS, message);
}
/**
* 成功返回数据和消息
*
* @param message 待返回消息
* @param data 带返回数据
* @return ApiResponse
*/
protected ApiResponse success(String message, Object data) {
return new ApiResponse(BaseApiCode.SUCCESS, message, data);
}
/**
* 失败返回
*
* @return ApiResponse
*/
protected ApiResponse failed() {
return ApiResponse.failed();
}
/**
* 失败返回数据
*
* @param data 待返回数据
* @return ApiResponse
*/
protected ApiResponse failed(Object data) {
return ApiResponse.failed(data);
}
/**
* 失败返回消息
*
* @param message 待返回成功消息
* @return ApiResponse
*/
protected ApiResponse failed(String message) {
return new ApiResponse(BaseApiCode.FAILED, message);
}
/**
* 失败返回数据和消息
*
* @param message 待返回消息
* @param data 带返回数据
* @return ApiResponse
*/
protected ApiResponse failed(String message, Object data) {
return new ApiResponse(BaseApiCode.FAILED, message, data);
}
/**
* 失败返回数据和消息
*
* @param apiCode 状态码
* @param data 带返回数据
* @return ApiResponse
*/
protected ApiResponse failed(BaseApiCode apiCode, Object data) {
return new ApiResponse(apiCode, apiCode.getCodeMsg(), data);
}
}
ClaireConstant : 还没写什么内容
package com.claire.common;
/**
* @author Claire
* 常量类
*/
public class ClaireConstant {
}
StoriesMapper
package com.claire.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.claire.bean.Stories;
/**
* @author Claire
*/
public interface StoriesMapper extends BaseMapper<Stories> {
}
StoriesService
package com.claire.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.claire.bean.Stories;
import com.claire.mapper.StoriesMapper;
import com.claire.utils.DataUtils;
import org.springframework.stereotype.Service;
/**
* @author Claire
*/
@Service
public class StoriesService extends ServiceImpl<StoriesMapper, Stories> {
public IPage<Stories> getTestStories(Long current,Long size){
return baseMapper.selectPage(new Page<>(current,size),null);
}
public IPage<Stories> getStories(Long current,Long size,Stories sto){
QueryWrapper<Stories> queryWrapper=new QueryWrapper<>();
//分页条件
queryWrapper.like(DataUtils.isNotEmptyStr(sto.getSTitle()),"s_title",sto.getSTitle());
queryWrapper.like(DataUtils.isNotEmptyStr(sto.getSAuthor()),"s_author",sto.getSAuthor());
queryWrapper.orderByDesc("create_time");
return baseMapper.selectPage(new Page<>(current,size),queryWrapper);
}
}
StoriesController
package com.claire.controller;
import com.claire.bean.Stories;
import com.claire.common.ApiResponse;
import com.claire.common.BaseController;
import com.claire.service.StoriesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author Claire
*/
@RestController
@RequestMapping("/stories")
public class StoriesController extends BaseController {
@Autowired
private StoriesService storiesService;
/**
* 获取所有的故事
* @param current
* @param size
* @param stories
* @return
*/
@RequestMapping(value = "/getStories",method = RequestMethod.POST,produces = "application/json;charset=UTF-8")
public ApiResponse getStories(Long current, Long size, @RequestBody Stories stories){
return success(storiesService.getStories(current,size,stories));
}
@RequestMapping("/testGetStories")
public ApiResponse testGetStories(@RequestParam Long current,@RequestParam Long size){
return success(storiesService.getTestStories(current,size));
}
}
到这里,运行,就可以看到
当然期间可能会出现很多的错误,可以去咨询下几乎万能的百度。
那么接下来我们来建立前端的框架,然后尝试把Springboot与Vue整合在一起
搭建Vue框架
①装node.js (自行百度)
②安装vue脚手架 (自行百度)
③创建Vue项目
vue init webpack (项目名称)
然后回车回车,安装路由(instal vue-router)那里选择y,其余的是否开启ESLint语法检测什么的就选n,一路往下就可以了
④运行这个项目
用VSCode打开它,Ctrl + ~打开终端,输入 npm install,装了淘宝镜像的可以选择 cnpm install 快一点
完成之后输入
npm run dev
,可以看到已经运行成功了,
去这网址就可以看到
Vue框架引入axios
引入这个是为了连接后台。
①安装axios
停止运行,回到终端,输入
cnpm(npm) install axios --save-dev
说一下后面这个-dev的作用
安装使用--save-dev简写为-D 的插件是被写入到 devDependencies 对象里面。
使用--save简写为-S 的插件是则被写入到 dependencies 对象里面
devDependencies 里面的插件只用于开发环境,不用于生产环境。
dependencies 则是需要发布到生产环境的。
②引入axios
在main.js中写入
import axios from "axios"
Vue.prototype.$http = axios;
axios.defaults.withCredentials = true
此时,main.js全貌:
接下来打开index.js
在proxyTable里面增加一些配置。本来后台的地址是8080,但是前端访问的地址也是8080,那么可以看到这里我写了8081(也可以写别的),因为端口会冲突,稍后会修改后台启动端口
proxyTable: {
'/api': {
target: 'http://localhost:8081', //后端请求服务域名和端口
changeOrigin: true, //设置请求头
pathRewrite: {
'^/api': '' //路径重写
}
}
},
Vue框架引入iView(可选)
这里我个人比较偏向于iView,当然也有很多人偏向于elementUI,都是可以的,不使用这个也是可以的。只是为了样式更方便写这个样子。
①安装
npm install view-design --save
②引入
依然是main.js,写入
import ViewUI from "view-design";
import "view-design/dist/styles/iview.css";
Vue.use(ViewUI);
Springboot与Vue的通信
接下来测试一下,前台调用后台接口。
首先前端调用:
①assets是一个资源文件夹,删除里面的图片,创建三个文件夹css、img、js,在js里新增一个story.js
import Axios from "axios"
let prefix = "/api"
export default {
/**
* 测试获取所有的故事
* @param {*} params
*/
getTestStories(params) {
return new Promise((resolve, rejects) => {
Axios.get(prefix + "/stories/testGetStories?current=" + params.current + "&&size=" + params.size)
.then(response => {
resolve(response.data);
})
.catch(error => {
console.log("Error", error);
})
})
}
}
修改HelloWorld.vue
<template>
<div class="hello">
<Button type="primary" @click="getTestStories">获取故事</Button>
</div>
</template>
<script>
import StoryService from "../assets/js/story";
export default {
name: "HelloWorld",
data() {
return {
params: {
current: 1,
size: 3
}, //获取故事的参数
stories: [] //获取到的故事
};
},
methods: {
getTestStories() {
StoryService.getTestStories(this.params).then(res => {
if (res.code == 0) {
this.stories = res.data.records;
console.log(this.stories);
} else {
this.$Message.error(res.msg);
}
});
}
}
};
</script>
<style scoped>
</style>
同时删除App.vue 里的
Springboot整合Vue跨域解决
在config文件夹新增WebMvcConfig文件
package com.claire.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author Claire
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 跨域支持
*
* @return CorsFilter
*/
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
/* 是否允许请求带有验证信息 */
corsConfiguration.setAllowCredentials(true);
/* 允许访问的客户端域名 */
corsConfiguration.addAllowedOrigin("*");
/* 允许服务端访问的客户端请求头 */
corsConfiguration.addAllowedHeader("*");
/* 允许访问的方法名,GET POST等 */
corsConfiguration.addAllowedMethod("*");
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
修改后台端口。在resources/application.yml里加入
启动项目,前后台都启动
打开localhost:8080
点击,可以看到在console打印台输出获取到的数据,修改一下HelloWorld.vue,用表格呈现
<template>
<div class="hello">
<Table stripe :columns="columns" :data="stories" style="width:1000px"></Table>
</div>
</template>
<script>
import StoryService from "../assets/js/story";
export default {
name: "HelloWorld",
data() {
return {
params: {
current: 1,
size: 3
}, //获取故事的参数
stories: [], //获取到的故事
columns: [
{
title: "id",
key: "sid"
},
{
title: "故事名称",
key: "stitle"
},
{
title: "故事作者",
key: "sauthor"
},
{
title: "故事内容",
key: "content"
},
{
title: "创建时间",
key: "createTime"
}
]
};
},
methods: {
getTestStories() {
StoryService.getTestStories(this.params).then(res => {
if (res.code == 0) {
this.stories = res.data.records;
console.log(this.stories);
} else {
this.$Message.error(res.msg);
}
});
}
},
created() {
this.getTestStories();
}
};
</script>
<style scoped>
</style>
最后呈现效果:
前端部分:
https://gitee.com/tangjinxia/union_front.git
后台部分:
https://gitee.com/tangjinxia/union.git
数据库:
链接:https://pan.baidu.com/s/1CeVU661NG8ylGZ1dCHfJ1w
提取码:ri0j