党建管理系统
1、软件版本
- jdk1.8
- mysql8.0
- node14.16.0
- navicat
- idea2021.3
##2、准备与创建vue项目
vue-cli安装:npm install -g @vue/cli
npm设置淘宝镜像加速:npm config set registry https://registry.npm.taobao.org
- 创建vue项目:
vue create vue
- 运行vue项目:
cd vue
npm run serve
- 安装ElementUI:
npm i element-ui -S
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';
import '../src/assets/gloable.css'
Vue.config.productionTip = false
Vue.use(ElementUI,{size:"small"});
new Vue({
router,
render: h => h(App)
}).$mount('#app')
Home.vue(第一节)
<el-button type="danger">{{ msg }}</el-button>
export default{
name:'Home',
components:{
Helloworld
},data(){
return{
msg:"hello world"
}
}
}
Home.vue(第二节)
<template>
<div style="height: 100%">
<el-container style="height: 100%; /*border: 1px solid #eee*/">
<el-aside width="217px" style="background-color: rgb(238, 241, 246);height: 100%">
<el-menu :default-openeds="['1', '3']" style="height: 100%">
<el-submenu index="1">
<template slot="title"><i class="el-icon-message"></i>导航一</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-menu"></i>导航二</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title"><i class="el-icon-setting"></i>导航三</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="3-1">选项1</el-menu-item>
<el-menu-item index="3-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="3-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="3-4">
<template slot="title">选项4</template>
<el-menu-item index="3-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="text-align: right; font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>查看</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</el-header>
<el-main>
<el-table :data="tableData">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
</el-table>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(10).fill(item)
}
}
}
</script>
App.vue(第二节)
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
#app{
height: 100%;
}
</style>
package.json
{
"name": "vue01",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"core-js": "^3.6.5",
"element-ui": "^2.15.6",
"vue": "^2.6.11",
"vue-router": "^3.2.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
gloable.css(第二节)
html,body,div{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
}
在main.js引入:import '../src/assets/gloable.css'
3、Vue后台整体布局完善
gloable.css(第二节)
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="3-1">选项1</el-menu-item>
<el-menu-item index="3-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="3-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="3-4">
<template slot="title">选项4</template>
<el-menu-item index="3-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<el-table :data="tableData">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
</el-table>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(10).fill(item),
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true
}
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
}
}
}
</script>
App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
#app{
min-height: 100vh;
}
</style>
main.js
保持不变
4、Vue页面主体布局完善
gloable.css(第三节)
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.ml-5{
margin-left: 5px;
}
.mr-5{
margin-right: 5px;
}
.pd-10{
padding: 10px 0;
}
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入查找内容" suffix-icon="el-icon-search"></el-input>
<el-button class="ml-5" type="primary">搜索</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
:page-sizes="[5, 10, 15, 20]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="400">
</el-pagination><!--分页栏-->
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(10).fill(item),
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
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';
import '../src/assets/gloable.css'
Vue.config.productionTip = false
Vue.use(ElementUI,{size:"mini"});
new Vue({
router,
render: h => h(App)
}).$mount('#app')
App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
#app{
min-height: 100vh;
}
</style>
5、SpringBoot框架搭建
pom里配置阿里云仓库(加速maven下载依赖,放在pom.xml文件最后)
<repositories>
<repository>
<id>nexus-aliyun</id>
<name>nexus-aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
配置application.properties
server.port=9090
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8
spring.datasource.username=root
spring.datasource.password=180918
写个测试接口
package com.kep.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
@GetMapping("/")
public String index(){
return "ok";
}
}
并导入vue项目到springboot文件夹中
6、SpringBoot集成Mybatis实现数据查询
application.properties改进为application.yml
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8
username: root
password: 180918
按照数据库三范式创建数据库表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLyuODru-1647074177441)(软工毕设/image/Snipaste_2022-02-07_11-53-34.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ox2gYV1m-1647074177442)(软工毕设/image/Snipaste_2022-02-07_11-54-02.png)]
用Mybatis来操作数据库
1、首先在java包下创建entity包
2、在entity包里创建实体类User,将数据库表中属性在实体类中声明出来
set和get方法一般是用来给类的成员变量赋值的,由于类的成员变量一般会声明为private的,其他的类是不能直接访问成员变量的,所以为了在类以外给该类的成员变量赋值或者取值,只有用声明为public的set和get方法来实现set和get方法是用于封装的 所以一般只在private中用 当然你如果不是在private中用也没关系 代码也是可以执行的。
public void setA(int a){this.a = a; }是传进去一个int类型的实参a把它赋值给类里的形参a
public void setA(){this.a = a; }相当于执行了一条a=a的代码 都是形参;
public int getA(int a){this.a = a;}
public int get(){this.a = a;}
这两条代码都通不过编译 会提示没有返回值。
package com.kep.springboot.entity;
public class User {
private Integer id;
private String username;
private String password;
private String nickname;
private String email;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
可使用lombok插件简化代码
package com.kep.springboot.entity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
private String password;
private String nickname;
private String email;
private String address;
}
3、在java包下创建mapper包,在包下创建数据库接口(注解@select由Mybatis提供,注解@Mapper将UserMapper这个bean注入springboot)
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
}
4、在SpringbootApplication.java里查询出所有数据(@RestController注解与@GetMapper组合使用,@RestController标识SpringbootApplication是一个接口,然后@GetMapper用于查询所有数据。)
package com.kep.springboot;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@SpringBootApplication
public class SpringbootApplication {
@Autowired
private UserMapper userMapper;
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
@GetMapping("/")
public List<User> index(){
return userMapper.findAll();
}
}
但是我们并不应该这么写,而是:
4、在java包下com.kep.springboot包里新建controller包,在controller包里新建类UserController.java,再将SpringbootApplication.java内的内容转移到到UserController里。此时:
SpringbootApplication.java
package com.kep.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
UserController.java
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/")
public List<User> index(){
return userMapper.findAll();
}
}
7、SpringBoot实现增删改查
1、接口UserMapper.java中添加插入注解@Insert
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
}
2、在UserController.java类中添加注解@PostMapper,写上save接口,注解@RequestBody是把前台传来的json对象转成后台的java对象
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@PostMapping
public Integer save(@RequestBody User user){
return userMapper.insert(user);
}
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
}
3、下载postman工具,用于测试接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ots5vfLk-1647074177443)(软工毕设/image/Snipaste_2022-02-07_14-48-31.png)]
send成功插入数据后:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkzKLwH3-1647074177444)(软工毕设/image/Snipaste_2022-02-07_14-52-48.png)]
4、在包下添加service包,创建UserService类,加入注解@Service
package com.kep.springboot.service;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int save(User user){
if (user.getId()==null){ //user没有id,则表示是新增
return userMapper.insert(user);
}else { //否则为更新
return userMapper.update(user);
}
}
}
在UserMapper接口中并用postman测试(静态更新操作)
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
@Update("update sys_user set username = #{username},password = #{password},nickname=#{nickname},email=#{email}," +
"phone=#{phone},address=#{address} where id = #{id}")
int update(User user);
}
5、改为动态sql更新数据库
先在resources下建文件夹mapper,创建文件User.xml,将原来@update注解删除
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTO Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kep.springboot.mapper.UserMapper">
<update id="update">
update sys_user
<set>
<if test="username !=null">
username = #{username},
</if>
<!--<if test="password !=null">
password = #{password}
</if>-->
<if test="nickname !=null">
nickname = #{nickname},
</if>
<if test="email !=null">
email = #{email},
</if>
<if test="phone !=null">
phone = #{phone},
</if>
<if test="address !=null">
address = #{address}
</if>
</set>
<where>
id = #{id}
</where>
</update>
</mapper>
删除后UserMapper.java
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
int update(User user);
}
最后在application.yml文件中添加扫描所有mybatis的xml文件
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8
username: root
password: 180918
mybatis:
mapper-locations: classpath:mapper/*.xml #扫描所有mybatis的xml文件
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6JVdYFG-1647074177444)(软工毕设/image/Snipaste_2022-02-07_16-28-48.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7y7Yugc-1647074177444)(软工毕设/image/Snipaste_2022-02-07_16-29-12.png)]
6、删除功能(用注释实现)
在UserController.java中
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public Integer save(@RequestBody User user){
// 新增或者更新
return userService.save(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
//删除
@DeleteMapping("/{id}")
public Integer delete(@PathVariable Integer id) { //表示url参数
return userMapper.deleteById(id);
}
}
在UserMapper.java中
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
int update(User user);
@Delete("delete from sys_user where id = #{id}")
Integer deleteById(@Param("id") Integer id);
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8TBLEh2-1647074177445)(软工毕设/image/Snipaste_2022-02-07_16-46-59.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMmofhUg-1647074177445)(软工毕设/image/Snipaste_2022-02-07_16-47-31.png)]
8、SpringBoot实现分页查询
1、纯手写分页查询
在UserController里
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public Integer save(@RequestBody User user){
// 新增或者更新
return userService.save(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
//删除
@DeleteMapping("/{id}")
public Integer delete(@PathVariable Integer id) { //表示url参数
return userMapper.deleteById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
@GetMapping("/page")
public Map<String, Object> findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize){
pageNum = (pageNum -1) * pageSize;
List<User> data=userMapper.selectPage(pageNum,pageSize);
Integer total = userMapper.selectTotal();
Map<String,Object> res = new HashMap<>();
res.put("data",data);
res.put("total",total);
return res;
}
}
在UserMapper类里
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
int update(User user);
@Delete("delete from sys_user where id = #{id}")
Integer deleteById(@Param("id") Integer id);
@Select("select * from sys_user limit #{pageNum},#{pageSize}")
List<User> selectPage(Integer pageNum, Integer pageSize);
@Select("select count(*) from sys_user")
Integer selectTotal();
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZsXjX7hj-1647074177445)(软工毕设/image/Snipaste_2022-02-07_17-34-26.png)]
分页与Home.vue结合
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入查找内容" suffix-icon="el-icon-search"></el-input>
<el-button class="ml-5" type="primary">搜索</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
:page-sizes="[5, 10, 15, 20]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="400">
</el-pagination><!--分页栏-->
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(10).fill(item),
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
fetch("http://localhost:9090/user/page?pageNum=1&pageSize=2").then(res => res.json()).then(res => {
console.log(res)
})
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
产生了前端跨域问题
Access to fetch at ‘http://localhost:9090/user/page?pageNum=1&pageSize=2’ from origin ‘http://192.168.10.3:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
跨域是当它请求的一个资源是从一个与它本身提供的第一个资源的不同的域名时,一个资源会发起一个跨域HTTP请求(Cross-site HTTP request)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HPCUfR5A-1647074177446)(软工毕设/image/Snipaste_2022-02-07_18-08-35.png)]
因此需要springboot跨域配置
在包下创建config包,包里创建CorsConfig.java
package com.kep.springboot.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;
@Configuration
public class CorsConfig {
// 当前跨域请求最大有效时长。这里默认1天
private static final long MAX_AGE = 24 * 60 * 60;
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("http://localhost:8080"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(MAX_AGE);
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
忽略某个字段,不展示给前端
package com.kep.springboot.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
@JsonIgnore
private String password;
private String nickname;
private String email;
private String phone;
private String address;
}
动态sql改变后
对于UserController,用Map封装起来
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public Integer save(@RequestBody User user){
// 新增或者更新
return userService.save(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
//删除
@DeleteMapping("/{id}")
public Integer delete(@PathVariable Integer id) { //表示url参数
return userMapper.deleteById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
@GetMapping("/page")
public Map<String, Object> findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize){
pageNum = (pageNum -1) * pageSize;
List<User> data=userMapper.selectPage(pageNum,pageSize);
Integer total = userMapper.selectTotal();
Map<String,Object> res = new HashMap<>();
res.put("data",data);
res.put("total",total);
return res;
}
}
对于Home.vue,进行修改后
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入查找内容" suffix-icon="el-icon-search"></el-input>
<el-button class="ml-5" type="primary">搜索</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[2, 5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:2,
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
fetch("http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize).then(res => res.json()).then(res => {
console.log(res)
this.tableData = res.data
this.total = res.total
})
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
SpringBoot针对于username进行模糊查询
对于Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入查找内容" suffix-icon="el-icon-search" v-model="username"></el-input>
<el-button class="ml-5" type="primary" @click="load">搜索</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
fetch("http://localhost:9090/user/page?pageNum="+this.pageNum+"&pageSize="+this.pageSize+"&username="+this.username).then(res => res.json()).then(res => {
console.log(res)
this.tableData = res.data
this.total = res.total
})
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
对于UserMapper.java
package com.kep.springboot.mapper;
import com.kep.springboot.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * from sys_user")
List<User> findAll();
@Insert("INSERT into sys_user(username,password,nickname,email,phone,address) VALUES (#{username},#{password}," +
"#{nickname},#{email},#{phone},#{address})")
int insert(User user);
int update(User user);
@Delete("delete from sys_user where id = #{id}")
Integer deleteById(@Param("id") Integer id);
@Select("select * from sys_user where username like #{username} limit #{pageNum},#{pageSize}")
List<User> selectPage(Integer pageNum, Integer pageSize,String username);
@Select("select count(*) from sys_user where username like #{username}")
Integer selectTotal(String username);
}
对于UserController.java
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public Integer save(@RequestBody User user){
// 新增或者更新
return userService.save(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
//删除
@DeleteMapping("/{id}")
public Integer delete(@PathVariable Integer id) { //表示url参数
return userMapper.deleteById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
@GetMapping("/page")
public Map<String, Object> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username){
pageNum = (pageNum -1) * pageSize;
username = "%"+username+"%";
List<User> data=userMapper.selectPage(pageNum,pageSize,username);
Integer total = userMapper.selectTotal(username);
Map<String,Object> res = new HashMap<>();
res.put("data",data);
res.put("total",total);
return res;
}
}
下节实现多条件查询
9、SpringBoot集成Mybatis-plus和SwaggerUI
依赖引入Mybatis-plus
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
在config包下创建MybatisPlusConfig.java,再加上@MapperScan,而此时UserMapper.java中@Mapper不需要了
package com.kep.springboot.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.kep.springboot.mapper")
public class MybatisPlusConfig {
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
用Mybatis-plus框架来实现新增与修改
UserService.java
package com.kep.springboot.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService extends ServiceImpl<UserMapper,User> {
public boolean saveUser(User user) {
// if (user.getId() ==null){
// return save(user); //mybatis-plus提供的方法,表示插入数据
// }else {
// return updateById(user);
// }
// }
return saveOrUpdate(user);
// @Autowired
// private UserMapper userMapper;
// public int save(User user){
// if (user.getId()==null){ //user没有id,则表示是新增
// return userMapper.insert(user);
// }else { //否则为更新
// return userMapper.update(user);
// }
// }
}
}
UserController.java
package com.kep.springboot.controller;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
List<User> all= userMapper.findAll();
return all;
}
//删除
@DeleteMapping("/{id}")
public Integer delete(@PathVariable Integer id) { //表示url参数
return userMapper.deleteById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
@GetMapping("/page")
public Map<String, Object> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username){
pageNum = (pageNum -1) * pageSize;
username = "%"+username+"%";
List<User> data=userMapper.selectPage(pageNum,pageSize,username);
Integer total = userMapper.selectTotal(username);
Map<String,Object> res = new HashMap<>();
res.put("data",data);
res.put("total",total);
return res;
}
}
因为Mybatis-plus无法识别数据库表名称
错误示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pAYGy5k0-1647074177447)(软工毕设/image/Snipaste_2022-02-07_23-02-26.png)]
因此:
User.java
package com.kep.springboot.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Data
@TableName(value = "sys_user")
public class User {
@TableId(type = IdType.AUTO) //指定主键是id
private Integer id;
private String username;
@JsonIgnore
private String password;
private String nickname;
private String email;
private String phone;
private String address; //驼峰写法也可以_后面第一个字母大写
}
集成Swagger-UI,用Swagger测试接口,Swagger是一个后台生成的接口文档,在springboot项目中集成swagger-ui
1、Swagger配置类
第一步,需要在pom中引入相应的配置,这里使用2.7.0的版本。需要注意的是2.7.0和2.8.0的版本在界面风格上差异很大,如果感兴趣,可以试试2.8.0以上的版本,我比较青睐使用2.7.0及以下的版本,因为界面比较清爽。
第一步 引入pom
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
第二步
在代码中加入相应的配置,新建config包,写入swaggerConfig配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* 创建API应用
* apiInfo() 增加API相关信息
* 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
* 本例采用指定扫描的包路径来定义指定要建立API的目录。
*
* @return
*/
@Bean
public Docket restApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("标准接口")
.apiInfo(apiInfo("Spring Boot中使用Swagger2构建RESTful APIs", "1.0"))
.useDefaultResponseMessages(true)
.forCodeGeneration(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.xqnode.learning.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 创建该API的基本信息(这些基本信息会展现在文档页面中)
* 访问地址:http://ip:port/swagger-ui.html
*
* @return
*/
private ApiInfo apiInfo(String title, String version) {
return new ApiInfoBuilder()
.title(title)
.description("更多请关注: https://blog.csdn.net/xqnode")
.termsOfServiceUrl("https://blog.csdn.net/xqnode")
.contact(new Contact("xqnode", "https://blog.csdn.net/xqnode", "xiaqingweb@163.com"))
.version(version)
.build();
}
}
此时访问http://localhost:9090/swagger-ui.html
若改用Swagger3.0版本,则:
在pom.xml中引入依赖
<!--swagger-->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
在SwaggerConfig.java中
package com.kep.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
@EnableOpenApi
public class SwaggerConfig {
/**
* 创建API应用
* apiInfo() 增加API相关信息
* 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
* 本例采用指定扫描的包路径来定义指定要建立API的目录。
*
* @return
*/
@Bean
public Docket restApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("标准接口")
.apiInfo(apiInfo("Spring Boot中使用Swagger2构建RESTful APIs", "1.0"))
.useDefaultResponseMessages(true)
.forCodeGeneration(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.kep.springboot.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 创建该API的基本信息(这些基本信息会展现在文档页面中)
* 访问地址:http://ip:port/swagger-ui.html
*
* @return
*/
private ApiInfo apiInfo(String title, String version) {
return new ApiInfoBuilder()
.title(title)
.description("党建管理系统Swagger-UI")
.termsOfServiceUrl("https://blog.csdn.net/xqnode")
.contact(new Contact("xqnode", "https://blog.csdn.net/xqnode", "xiaqingweb@163.com"))
.version(version)
.build();
}
}
此时访问http://localhost:9090/swagger-ui/index.html来实现对后台接口测试
用Mybatis-plus的方式实现分页查询
则UserController.java变为:
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
IPage<User> userPage = userService.page(page, queryWrapper);
return userPage;
}
}
出现错误:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSxeIeUD-1647074177448)(软工毕设/image/Snipaste_2022-02-08_13-26-01.png)]
原因是与UserMapper.java中的SelectPage重复了,将UserMapper中用注解写的全都注释掉后
将UserMapper.java写成
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username){
IPage<User> page =new Page<>(pageNum,pageSize);
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
IPage<User> userPage = userService.page(page, null);
// queryWrapper.like("username",username);
return userPage;
}
}
postman测试结果为:
{
"records": [
{
"id": 1,
"username": "admin",
"nickname": "管理员",
"email": "admin@qq.com",
"phone": "1388888888",
"address": "湖北省武汉市"
},
{
"id": 2,
"username": "ke",
"nickname": "柯鹏",
"email": "k180918@163.com",
"phone": "13872062736",
"address": "湖北省黄石市"
}
],
"total": 8,
"size": 2,
"current": 1,
"orders": [],
"optimizeCountSql": true,
"searchCount": true,
"countId": null,
"maxLimit": null,
"pages": 4
}
但我们只需要record与total内容
最后我们将UserController改写为:
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username",username);
IPage<User> userPage = userService.page(page,queryWrapper);
return userPage;
}
}
模糊查询测试结果为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5p5qCbgp-1647074177448)(软工毕设/image/Snipaste_2022-02-08_13-48-33.png)]
并且Mybatis-plus框架可以方便实现多条件模糊查询
在UserController中
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username,
@RequestParam String nickname){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username",username);
queryWrapper.like("nickname",nickname);
//或者 queryWrapper.and(w -> w.like("nickname",nickname));
IPage<User> userPage = userService.page(page,queryWrapper);
return userPage;
}
}
可实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jloBha0b-1647074177448)(软工毕设/image/Snipaste_2022-02-08_14-04-45.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yiHhcO5H-1647074177449)(软工毕设/image/Snipaste_2022-02-08_14-05-04.png)]
如果将并查询换成或查询后(例如将address加为或查询)
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam String username,
@RequestParam String nickname,
@RequestParam String address){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username",username);
queryWrapper.like("nickname",nickname);
queryWrapper.or().like("address",address);
//或者 queryWrapper.and(w -> w.like("nickname",nickname));
IPage<User> userPage = userService.page(page,queryWrapper);
return userPage;
}
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OyOy1Bld-1647074177449)(软工毕设/image/Snipaste_2022-02-08_14-10-55.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyxbZThy-1647074177449)(软工毕设/image/Snipaste_2022-02-08_14-11-29.png)]
若并查询,则若username与nickname为空,只按address查询应该可全部查到
因此改进UserController.java(加上默认值与判断语句)
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam(defaultValue = "") String username,
@RequestParam(defaultValue = "") String nickname,
@RequestParam(defaultValue = "") String address){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (!"".equals(username)) {
queryWrapper.like("username", username);
}
if (!"".equals(nickname)) {
queryWrapper.like("nickname", nickname);
}
if (!"".equals(address)) {
queryWrapper.like("address", address);
}
//或者 queryWrapper.and(w -> w.like("nickname",nickname));
IPage<User> userPage = userService.page(page,queryWrapper);
return userPage;
}
}
实现效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbQ8Jnzy-1647074177450)(软工毕设/image/Snipaste_2022-02-08_14-37-55.png)]
10、Vue实现增删改查
我们之前用的fetch来实现前端页面显示后台数据,现在用了Mybatis-plus框架后,我们改用axios
下载axios,在idea中前端项目文件中Terminal里写上 npm i axios -S
在前台部分新建文件夹utils,utils下新建文件request.js,request.js封装
import axios from 'axios'
const request = axios.create({
baseURL: '/api', // 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加 '/api'了,否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
timeout: 5000
})
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// config.headers['token'] = user.token; // 设置请求头
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
在main.js中引入request.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';
import '../src/assets/gloable.css'
import request from "./utils/request";
Vue.config.productionTip = false
Vue.use(ElementUI,{size:"mini"});
Vue.prototype.request=request
new Vue({
router,
render: h => h(App)
}).$mount('#app')
在Home.vue中修改fetch部分
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入查找内容" suffix-icon="el-icon-search" v-model="username"></el-input>
<el-button class="ml-5" type="primary" @click="load">搜索</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("http://localhost:9090/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
此时可实现前后端数据绑定
实现新增功能
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入用户名查找" suffix-icon="el-icon-search" v-model="username" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
<el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></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="save">确 定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
nickname:"",
form:{},
dialogFormVisible:false,
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("http://localhost:9090/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save(){
request.post("http://localhost:9090/user",this.form).then(res => {
if (res){
this.$message.success("保存成功!")
this.dialogFormVisible = false
}else {
this.$message.error("保存失败!")
}
})
},
reset(){
this.username = ""
this.load()
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
this.$refs.select.blur()//解决选择框回车后展示下拉列表问题
},
handleAdd(){
this.dialogFormVisible = true //打开弹窗
this.form = {} //把form对象置空
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
此时新增的数据得实时显示,因此需要倒叙排列显示
实现编辑功能
由于新增和修改都是post接口
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入用户名查找" suffix-icon="el-icon-search" v-model="username" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-button type="danger">删除 <i class="el-icon-remove-outline"></i></el-button>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
<el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></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="save">确 定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
nickname:"",
form:{},
dialogFormVisible:false,
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save(){
request.post("/user",this.form).then(res => {
if (res){
this.$message.success("保存成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("保存失败!")
}
})
},
reset(){
this.username = ""
this.load()
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
this.$refs.select.blur()//解决选择框回车后展示下拉列表问题
},
handleAdd(){
this.dialogFormVisible = true //打开弹窗
this.form = {} //把form对象置空
},
handleEdit(row){
this.form = row
this.dialogFormVisible = true
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
删除功能
用delete接口
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入用户名查找" suffix-icon="el-icon-search" v-model="username" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg">
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@confirm="del(scope.row.id)">
<el-button type="danger" slot="reference" >删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
<el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></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="save">确 定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
nickname:"",
form:{},
dialogFormVisible:false,
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save(){
request.post("/user",this.form).then(res => {
if (res){
this.$message.success("保存成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("保存失败!")
}
})
},
del(id){
request.delete("/user/"+id).then(res => {
if (res){
this.$message.success("删除成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("删除失败!")
}
})
},
reset(){
this.username = ""
this.load()
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
this.$refs.select.blur()//解决选择框回车后展示下拉列表问题
},
handleAdd(){
this.dialogFormVisible = true //打开弹窗
this.form = {} //把form对象置空
},
handleEdit(row){
this.form = row
this.dialogFormVisible = true
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
批量删除功能
用element-ui中Checkbox
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入用户名查找" suffix-icon="el-icon-search" v-model="username" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-button type="danger" @click="delBatch">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
<el-button type="primary">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@confirm="del(scope.row.id)">
<el-button type="danger" slot="reference" >删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
<el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></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="save">确 定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
nickname:"",
form:{},
dialogFormVisible:false,
multipleSelection:[],
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save(){
request.post("/user",this.form).then(res => {
if (res){
this.$message.success("保存成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("保存失败!")
}
})
},
del(id){
request.delete("/user/"+id).then(res => {
if (res){
this.$message.success("删除成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("删除失败!")
}
})
},
handleSelectionChange(val){
console.log(val)
this.multipleSelection = val
},
delBatch(){
let ids = this.multipleSelection.map(v => v.id) //将多选获取的对象扁平化处理,即[{},{},{}] => [1,2,3]
request.post("/user/del/batch",ids).then(res => {
if (res){
this.$message.success("批量删除成功!")
this.load()
}else {
this.$message.error("批量删除失败!")
}
})
},
reset(){
this.username = ""
this.load()
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
this.$refs.select.blur()//解决选择框回车后展示下拉列表问题
},
handleAdd(){
this.dialogFormVisible = true //打开弹窗
this.form = {} //把form对象置空
},
handleEdit(row){
this.form = row
this.dialogFormVisible = true
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
UserController.java
package com.kep.springboot.controller;
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.kep.springboot.entity.User;
import com.kep.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//新增和修改
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveUser(user);
}
//查询所有数据
@GetMapping
public List<User> index(){
return userService.list();
}
//删除
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) { //表示url参数
return userService.removeById(id);
}
//批量删除
@PostMapping("/del/batch")
public boolean deleteBatch(@RequestBody List<Integer> ids) { //表示url参数
return userService.removeBatchByIds(ids);
}
//分页查询
//接口路径:/user/page
//@RequestParam接收 pageNum=1&pageSize=10
//limit第一个参数 = (pageNum - 1) * pageSize
//pageSize
//selectTotal记录数据总条数
//用Map封装起来
// @GetMapping("/page")
// public Map<String, Object> findPage(@RequestParam Integer pageNum,
// @RequestParam Integer pageSize,
// @RequestParam String username){
// pageNum = (pageNum -1) * pageSize;
// username = "%"+username+"%";
// List<User> data=userMapper.selectPage(pageNum,pageSize,username);
// Integer total = userMapper.selectTotal(username);
// Map<String,Object> res = new HashMap<>();
// res.put("data",data);
// res.put("total",total);
// return res;
// }
// 分页查询 - mybatis-plus的方式
@GetMapping("/page")
public IPage<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam(defaultValue = "") String username,
@RequestParam(defaultValue = "") String nickname,
@RequestParam(defaultValue = "") String address){
IPage<User> page =new Page<>(pageNum,pageSize);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (!"".equals(username)) {
queryWrapper.like("username", username);
}
if (!"".equals(nickname)) {
queryWrapper.like("nickname", nickname);
}
if (!"".equals(address)) {
queryWrapper.like("address", address);
}
queryWrapper.orderByDesc("id");//新增数据倒叙排列
//或者 queryWrapper.and(w -> w.like("nickname",nickname));
IPage<User> userPage = userService.page(page,queryWrapper);
return userPage;
}
}
新增批量删除接口,并改为@PostMapping
新增删除前询问:
Home.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<el-menu :default-openeds="['1', '3']" style="min-height: 100%;overflow-x: hidden"
background-color="rgb(139 71 38)"
text-color="#fff"
active-text-color="#ffd04b"
:collapse-transition="false"
:collapse="isCollapse"><!--关掉菜单本身动画-->
<div style="height: 60px;line-height: 60px;text-align: center">
<img src="../assets/logo.png" alt="" style="width: 19px;position: relative;top: 5px;margin-right: 3px">
<b style="color: white" v-show="logoTextShow">后台管理系统</b>
</div>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title" >导航一</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title" >导航二</span>
</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc;line-height: 60px;display: flex">
<div style="flex: 1;font-size: 20px">
<span :class="collapseBtnClass" style="cursor: pointer" @click="collapse"></span>
</div>
<el-dropdown style="width: 70px;cursor: pointer">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>王小虎</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-main>
<div style="margin-bottom: 30px">
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入用户名查找" suffix-icon="el-icon-search" v-model="username" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div><!--搜索框-->
<div style="margin: 10px 0">
<el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i> </el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm="delBatch">
<el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
</el-popconfirm>
<el-button type="primary" class="ml-5">导入 <i class="el-icon-bottom"></i> </el-button>
<el-button type="primary">导出 <i class="el-icon-top"></i> </el-button>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column prop="id" label="ID" width="60">
</el-table-column>
<el-table-column prop="username" label="用户名" width="140">
</el-table-column>
<el-table-column prop="nickname" label="昵称" width="120">
</el-table-column>
<el-table-column prop="email" label="邮箱">
</el-table-column>
<el-table-column prop="phone" label="电话">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column label="修改操作">
<template slot-scope="scope">
<el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@confirm="del(scope.row.id)">
<el-button type="danger" slot="reference" >删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
<el-dialog title="用户信息" :visible.sync="dialogFormVisible" width="30%">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></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="save">确 定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import request from "../utils/request";
export default {
name: 'Home',
data(){
return {
tableData:[],
total:0,
pageNum:1,
pageSize:5,
phone:"",
username:"",
nickname:"",
form:{},
dialogFormVisible:false,
multipleSelection:[],
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
headerBg:'headerBg'
}
},
created() {
//请求分页查询数据
this.load()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
load(){
request.get("/user/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
username:this.username
}
}).then(res =>{
console.log(res)
this.tableData = res.records
this.total = res.total
})
},
save(){
request.post("/user",this.form).then(res => {
if (res){
this.$message.success("保存成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("保存失败!")
}
})
},
del(id){
request.delete("/user/"+id).then(res => {
if (res){
this.$message.success("删除成功!")
this.dialogFormVisible = false
this.load()
}else {
this.$message.error("删除失败!")
}
})
},
handleSelectionChange(val){
console.log(val)
this.multipleSelection = val
},
delBatch(){
let ids = this.multipleSelection.map(v => v.id) //将多选获取的对象扁平化处理,即[{},{},{}] => [1,2,3]
request.post("/user/del/batch",ids).then(res => {
if (res){
this.$message.success("批量删除成功!")
this.load()
}else {
this.$message.error("批量删除失败!")
}
})
},
reset(){
this.username = ""
this.load()
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
this.$refs.select.blur()//解决选择框回车后展示下拉列表问题
},
handleAdd(){
this.dialogFormVisible = true //打开弹窗
this.form = {} //把form对象置空
},
handleEdit(row){
this.form = row
this.dialogFormVisible = true
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
}
}
}
</script>
<style>
.headerBg{
background-color: #eee!important;
}
</style>
这样一来,增删改查就已经全部实现!💪
11、SpringBoot代码生成器
1、首先引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
还有一个依赖,因为它是默认 Velocity 为模板引擎
<!-- 模板引擎Velocity -->
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
2、创建文件夹utils,在文件夹中创建CodeGenerator.java
package com.kep.springboot.utils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
/**
* mp代码生成器
* by kppackage com.kep.springboot.utils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import java.util.Collections;
/**
* mp代码生成器
* by kp
* @since 2022-02-09
*/
public class CodeGenerator {
public static void main(String[] args) {
generate();
}
private static void generate(){
FastAutoGenerator.create("jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8", "root", "180918")
.globalConfig(builder -> {
builder.author("kp") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D:\\Intellij IDEA\\dangjian\\src\\main\\java\\"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.kep.springboot") // 设置父包名
.moduleName(null) // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D:\\Intellij IDEA\\dangjian\\src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("sys_user") // 设置需要生成的表名
.addTablePrefix("t_", "sys_"); // 设置过滤表前缀
})
// .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
* @since 2022-02-09
*/
public class CodeGenerator {
public static void main(String[] args) {
generate();
}
private static void generate(){
FastAutoGenerator.create("jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8", "root", "180918")
.globalConfig(builder -> {
builder.author("kp") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D:\\Intellij IDEA\\dangjian\\src\\main\\java\\"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.kep.springboot") // 设置父包名
.moduleName("") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D:\\Intellij IDEA\\dangjian\\src\\main\\resources\\mapper\\")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("sys_user") // 设置需要生成的表名
.addTablePrefix("t_", "sys_"); // 设置过滤表前缀
})
// .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
执行main函数后,自动生成代码
之后在External libraries中的controller.java.vm复制到resources中的templates中修改代码加入crud功能
package ${package.Controller};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import $!{package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
/**
* <p>
* $!{table.comment} 前端控制器
* </p>
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Resource
private ${table.serviceName} ${table.entityPath}Service;
@PostMapping
public boolean save(@RequestBody ${entity} ${table.entityPath}){
// 新增或者更新
return ${table.entityPath}Service.saveOrUpdate(${table.entityPath});
}
//删除
@DeleteMapping("/{id}")
public Boolean delete(@PathVariable Integer id) {
return ${table.entityPath}Service.removeById(id);
} /*${table.entityPath}==user*/
//批量删除
@PostMapping("/del/batch")
public boolean deleteBatch(@RequestBody List<Integer> ids) { //表示url参数
return ${table.entityPath}Service.removeBatchByIds(ids);
}
//查询
@GetMapping
public List<${entity}> findAll() {
return ${table.entityPath}Service.list();
}
//根据id查询
@GetMapping("/{id}")
public ${entity} findOne(@PathVariable Integer id) {
return ${table.entityPath}Service.getById(id);
}
//分页查询
@GetMapping("/page")
public Page<${entity}> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");//新增数据倒叙排列
return ${table.entityPath}Service.page(new Page<>(pageNum, pageSize),queryWrapper);
}
}
#end
由代码生成器生成的UserController.java为
package com.kep.springboot.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kep.springboot.entity.User;
import com.kep.springboot.service.IUserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author kp
* @since 2022-02-09
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
@PostMapping
public boolean save(@RequestBody User user){
// 新增或者更新
return userService.saveOrUpdate(user);
}
//删除
@DeleteMapping("/{id}")
public Boolean delete(@PathVariable Integer id) {
return userService.removeById(id);
} /*user==user*/
//批量删除
@PostMapping("/del/batch")
public boolean deleteBatch(@RequestBody List<Integer> ids) { //表示url参数
return userService.removeBatchByIds(ids);
}
//查询
@GetMapping
public List<User> findAll() {
return userService.list();
}
//根据id查询
@GetMapping("/{id}")
public User findOne(@PathVariable Integer id) {
return userService.getById(id);
}
//分页查询
@GetMapping("/page")
public Page<User> findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam(defaultValue = "") String username,
@RequestParam(defaultValue = "") String nickname,
@RequestParam(defaultValue = "") String address) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (!"".equals(username)) {
queryWrapper.like("username", username);
}
if (!"".equals(nickname)) {
queryWrapper.like("nickname", nickname);
}
if (!"".equals(address)) {
queryWrapper.like("address", address);
}
queryWrapper.orderByDesc("id");//新增数据倒叙排列
return userService.page(new Page<>(pageNum, pageSize),queryWrapper);
}
}
在前台测试后功能正常👍
12、Vue使用路由
我们将Home.vue改名为Manage.vue,管理界面,将界面分为导航栏和主体,而导航栏应不变,只变主体部分,把里面的视图作为子路由,根据子路由更换主界面
下载vuex,在terminal写npm i vuex -S
版本要是3.6.2
在src下创建文件夹store,文件夹内创建index.js
import Vue from 'vue';
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
currentPathName:''
},
mutations:{
setPath(state){
state.currentPathName = localStorage.getItem("currentPathName")
}
}
})
export default store
引入vuex,在main.js中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from "./store"
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import '../src/assets/gloable.css'
import request from "./utils/request";
Vue.config.productionTip = false
Vue.use(ElementUI,{size:"mini"});
Vue.prototype.request=request
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Manage.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
component: () => import('../views/Manage.vue'),
redirect:'/home',
children:[
{
path:'home', name:'首页', component: () =>import('../views/Home.vue')},
{path:'user',name:'用户管理',component: () => import('../views/User.vue')}
]
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
/*路由守卫*/
router.beforeEach((to, from, next) => {
localStorage.setItem("currentPathName",to.name) //设置当前的路由名称,为了在header组件中去使用
store.commit("setPath") //触发store的数据更新
next() //放行路由
})
export default router
Header.vue
computed:{
currentPathName(){
return this.$store.state.currentPathName; //需要监听的数据
}
},
watch:{ // 百度搜出的结果,监听路由变化
currentPathName(newVal,oldVal){
console.log(newVal)
}
},
// 使用
<el-breadcrumb separator="/" style="display: inline-block;margin-left: 10px">
<el-breadcrumb-item :to="'/'">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{ currentPathName }}</el-breadcrumb-item>
</el-breadcrumb>
13、SpringBoot和Vue实现导入导出
1、首先在pom.xml中导入hutool依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
2、在UserController.java中加入导出部分,在实体类中要加上注解@ToString
/**
* 导出接口
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception{
//从数据库查询出所有的数据
List<User> list = userService.list();
//通过工具类创建writer 写出到磁盘路径,我们就不导出到磁盘路径
//ExcelWriter writer = ExcelUtil.getWriter(fileUploadPath+"/用户信息.xlsx")
//在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
//自定义标题别名,根据实体类User
writer.addHeaderAlias("username","用户名");
writer.addHeaderAlias("password","密码");
writer.addHeaderAlias("nickname","昵称");
writer.addHeaderAlias("email","邮箱");
writer.addHeaderAlias("phone","电话");
writer.addHeaderAlias("address","地址");
writer.addHeaderAlias("createTime","创建时间");
writer.addHeaderAlias("avatarUrl","头像");
//一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list,true);
//设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("用户信息","UTF-8");
response.setHeader("Content-Disposition","attachment;filename="+ fileName +".xlsx");
//通过response获取到输出流,把writer里面的东西刷新到输出流里面去,最后通过输出流返回到浏览器
ServletOutputStream out = response.getOutputStream();
writer.flush(out,true);
out.close();
writer.close();
}
加入导入部分
/**
* excel 导入
* @param file
* @throws Exception
*/
@PostMapping("/import")
public Boolean imp(MultipartFile file) throws Exception{
InputStream inputStream =file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
//方式一:通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
// List<User> list = reader.readAll(User.class);
//方式二:忽略表头的中文,直接读取表的内容
List<List<Object>> list = reader.read(1);
List<User> users = CollUtil.newArrayList();
for (List<Object> row : list){
User user = new User();
user.setUsername(row.get(0).toString());
user.setPassword(row.get(1).toString());
user.setNickname(row.get(2).toString());
user.setEmail(row.get(3).toString());
user.setPhone(row.get(4).toString());
user.setAddress(row.get(5).toString());
users.add(user);
}
userService.saveBatch(users);
return true;
}
14、SpringBoot和Vue实现用户登录
1、在views文件夹内新增文件Login.vue
<template>
<div class="wrapper">
<div style="margin: 200px auto;background-color: #fff;width: 350px;height: 300px;padding: 20px;border-radius: 10px">
<div style="margin: 20px 0;text-align: center;font-size: 24px">
<b>用户登录</b></div>
<el-form :model="user" :rules="rules" ref="userForm">
<el-form-item prop="username" >
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input size="medium" style="margin: 10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0;text-align: center;">
<el-button type="primary" size="small" autocomplete="off" @click="login"style="margin-right: 20px">登录</el-button>
<el-button type="warning" size="small" autocomplete="off">注册</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
user:{},
rules:{
username:[
{required:true,message:'请输入用户名',trigger:'blur'},
{min:3,max:8,message: '长度在 3 到 8 个字符',trigger: 'blur'}
],
password:[
{required:true,message:'请输入密码',trigger:'blur'},
{min:5,max:12,message: '长度在 5 到 12 个字符',trigger: 'blur'}
]
}
}
},
methods:{
login(){
this.$refs['userForm'].validate((valid) =>{
if (valid){ //表单校验合法,合法才发送请求
this.request.post("/user/login",this.user).then(res =>{
if (!res){
this.$message.error("用户名或密码错误!")
}else {
this.$router.push("/")
}
})
}else {
return false;
}
})
}
}
}
</script>
<style>
.wrapper{
height: 100vh;
background-image: linear-gradient(to bottom right,#FC4668,#3F5EF8); /*从左上角到右上角渐变色*/
overflow: hidden;
}
</style>
2、在文件夹router里新建index.js,加上登录界面路由
{
path: '/login',
name:'Login',
component:() => import('../views/Login.vue')
}
3、在UserServiceImpl中加入登录验证功能
package com.kep.springboot.service.impl;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kep.springboot.controller.dto.UserDTo;
import com.kep.springboot.entity.User;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 服务实现类
* </p>
*
* @author kp
* @since 2022-02-09
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
private static final Log LOG = Log.get();
@Override
public boolean login(UserDTo userDTo) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDTo.getUsername());
queryWrapper.eq("password",userDTo.getPassword());
// 第一种方式,后台查询出多条相同的账号
// List<User> list = list(queryWrapper);
// return list.size()!=0;
// 第二种方式
try{
User one = getOne(queryWrapper);
return one !=null;
}catch (Exception e){
LOG.error(e);
return false;
}
}
}
4、在UserController中加入登陆验证
@PostMapping("/login")
public boolean login(@RequestBody UserDTo userDTo){
String username = userDTo.getUsername();
String password = userDTo.getPassword();
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
return false;
}
return userService.login(userDTo);
}
15、SpringBoot和Vue实现注册和异常处理
1、异常处理
设置不同的异常来进行不同的报错
1、创建文件夹common,文件夹下创建Result.java
package com.kep.springboot.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 接口返回包装类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private String code;
private String msg; //返回请求失败的原因
private Object data; //后台需要携带的数据
public static Result success(){
return new Result(Constants.CODE_200,"",null);
}
public static Result success(Object data){
return new Result(Constants.CODE_200,"",data);
}
public static Result error(String code,String msg){
return new Result(code,msg,null);
}
public static Result error(){
return new Result(Constants.CODE_500,"系统错误",null);
}
}
2、创建Constants.java接口
package com.kep.springboot.common;
public interface Constants {
String CODE_200 = "200"; //成功
String CODE_401 = "401"; //权限不足
String CODE_400 = "400"; //参数错误
String CODE_500 = "500"; //系统错误
String CODE_600 = "600"; //其他业务异常
}
3、创建异常处理的文件夹exception,里面创建两个类GlobalExceptionHandler
package com.kep.springboot.exception;
import com.kep.springboot.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 如果抛出的是ServiceException,则调用该方法
* @param se 业务异常
* @return Result
*/
@ExceptionHandler(ServiceException.class)
@ResponseBody
public Result handle(ServiceException se){
return Result.error(se.getCode(),se.getMessage());
}
}
4、创建类ServiceException
package com.kep.springboot.exception;
import lombok.Getter;
/**
* 自定义异常
*/
@Getter
public class ServiceException extends RuntimeException{
private String code;
public ServiceException(String code,String msg){
super(msg);
this.code = code;
}
}
5、UserServiceImpl
package com.kep.springboot.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kep.springboot.common.Constants;
import com.kep.springboot.controller.dto.UserDTO;
import com.kep.springboot.entity.User;
import com.kep.springboot.exception.ServiceException;
import com.kep.springboot.mapper.UserMapper;
import com.kep.springboot.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author kp
* @since 2022-02-09
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
private static final Log LOG = Log.get();
@Override
public UserDTO login(UserDTO userDTO) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDTO.getUsername());
queryWrapper.eq("password",userDTO.getPassword());
User one;
// 第一种方式,后台查询出多条相同的账号
// List<User> list = list(queryWrapper);
// return list.size()!=0;
// 第二种方式
try{
one = getOne(queryWrapper); //从数据库查询用户信息
}catch (Exception e){
LOG.error(e);
throw new ServiceException(Constants.CODE_500,"系统错误");
}
if (one !=null) {
BeanUtil.copyProperties(one,userDTO,true);
return userDTO;
}else {
throw new ServiceException(Constants.CODE_600,"用户名或密码错误");
}
}
}
6、IUserService接口
package com.kep.springboot.service;
import com.kep.springboot.controller.dto.UserDTO;
import com.kep.springboot.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author kp
* @since 2022-02-09
*/
public interface IUserService extends IService<User> {
UserDTO login(UserDTO userDTO);
}
7、在Login.vue中加入
methods:{
login(){
this.$refs['userForm'].validate((valid) =>{
if (valid){ //表单校验合法,合法才发送请求
this.request.post("/user/login",this.user).then(res =>{
if (res.code === '200' ){
localStorage.setItem("user",JSON.stringify(res.data)) //存储用户信息到浏览器
this.$router.push("/")
this.$message.success("登录成功!")
}else {
this.$message.error(res.msg)
}
})
}
});
8、在UserController.java中
@PostMapping("/login")
public Result login(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
return Result.error(Constants.CODE_400,"参数错误");
}
UserDTO dto = userService.login(userDTO);
return Result.success(dto);
}
9、在Header.vue中加上退出时的方法
<el-dropdown style="width: 120px;cursor: pointer">
<div style="display: inline-block">
<img src="../assets/IMG_0955(20220212-201455).png" alt="" style="width: 30px;border-radius: 50%;position: relative;top: 10px;right: 8px">
<!-- <i class="el-icon-setting" style="margin-right: 15px"></i>-->
<span>{{ user.nickname }}</span><i class="el-icon-arrow-down" style="margin-left: 5px"></i>
</div>
<el-dropdown-menu slot="dropdown" style="width: 100px;text-align: center">
<el-dropdown-item style="font-size: 14px;padding: 5px 0">个人信息</el-dropdown-item>
<!-- <el-dropdown-item style="font-size: 14px;padding: 5px">——</el-dropdown-item>-->
<el-dropdown-item style="font-size: 14px;padding: 5px 0">
<span style="text-decoration: none" @click="logout">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
data(){
return{
user:localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")):{}
}
},
logout(){ //退出时的方法:清除数据,导向登录界面
this.$router.push("/login")
localStorage.removeItem("user")
this.$message.success("退出成功!")
}
2、实现注册功能
1、首先创建Register.vue,可直接从Login.vue复制
<template>
<div class="wrapper">
<div style="margin: 80px auto;background-color: #fff;width: 350px;height: 550px;padding: 20px;border-radius: 10px">
<div style="margin: 20px 0;text-align: center;font-size: 24px">
<b>注 册</b></div>
<el-form :model="user" :rules="rules" ref="userForm">
<el-form-item prop="username" >
<el-input placeholder="请输入注册用户名" size="medium" style="margin: 6px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="请输入注册密码" size="medium" style="margin: 6px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input placeholder="请确认注册密码" size="medium" style="margin: 6px 0" prefix-icon="el-icon-finished" show-password v-model="user.confirmPassword"></el-input>
</el-form-item>
<el-form-item prop="nickname">
<el-input placeholder="请输入注册昵称" size="medium" style="margin: 6px 0" prefix-icon="el-icon-info" v-model="user.nickname"></el-input>
</el-form-item>
<el-form-item prop="email">
<el-input placeholder="请输入注册邮箱" size="medium" style="margin: 6px 0" prefix-icon="el-icon-message" v-model="user.email"></el-input>
</el-form-item>
<el-form-item prop="phone">
<el-input placeholder="请输入注册电话" size="medium" style="margin: 6px 0" prefix-icon="el-icon-phone" v-model="user.phone"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0;text-align: center;">
<el-button type="primary" size="small" autocomplete="off" @click="login"style="margin-right: 20px">注册</el-button>
<el-button type="warning" size="small" autocomplete="off" @click="$router.push('/login')">返回登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
user:{},
rules:{
username:[
{required:true,message:'请输入用户名',trigger:'blur'},
{min:2,max:8,message: '长度在 2 到 8 个字符',trigger: 'blur'}
],
password:[
{required:true,message:'请输入密码',trigger:'blur'},
{min:5,max:12,message: '长度在 5 到 12 个字符',trigger: 'blur'}
],
confirmPassword:[
{required:true,message:'请确认密码',trigger:'blur'},
{min:5,max:12,message: '长度在 5 到 12 个字符',trigger: 'blur'}
],
nickname:[
{required:true,message:'请输入昵称',trigger:'blur'},
{min:2,max:8,message: '长度在 2 到 8 个字符',trigger: 'blur'}
],
email:[
{required:true,message:'请输入邮箱',trigger:'blur'},
{min:8,max:25,message: '长度在 8 到 25 个字符',trigger: 'blur'}
],
phone:[
{required:true,message:'请输入电话',trigger:'blur'},
{min:11,max:11,message: '长度为11个字符',trigger: 'blur'}
]
}
}
},
methods:{
login(){
this.$refs['userForm'].validate((valid) =>{
if (valid){ //表单校验合法,合法才发送请求
if (this.user.password !== this.user.confirmPassword){
this.$message.error("两次输入的密码不一致")
return false;
}
this.request.post("/user/register",this.user).then(res =>{
if (res.code === '200' ){
this.$message.success("注册成功!")
}else {
this.$message.error(res.msg)
}
})
}
});
}
}
}
</script>
<style>
.wrapper{
height: 100vh;
background-image: linear-gradient(to bottom left,#FC4668,#3F5EF8); /*从左下角到右上角渐变色*/
overflow: hidden;
}
</style>
2、在路由中加入注册界面路由
{
path: '/register',
name:'Register',
component:() => import('../views/Register.vue')
},
3、在Login.vue的注册按钮中写上
<el-button type="warning" size="small" autocomplete="off" @click="$router.push('/register')">注册</el-button>
4、在UserController.java中加入注册接口
@PostMapping("/register")
public Result register(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
String nickname = userDTO.getNickname();
String email = userDTO.getEmail();
String phone = userDTO.getPhone();
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)||StrUtil.isBlank(nickname)||StrUtil.isBlank(email)||StrUtil.isBlank(phone)){
return Result.error(Constants.CODE_400,"参数错误");
}
return Result.success(userService.register(userDTO));
}
5、在IUserService.java接口中加上
User register(UserDTO userDTO);
6、在实现类UserServiceImpl.java中加上
@Override
public User register(UserDTO userDTO) {
User one = getUserInfo(userDTO);
if (one == null){
one = new User();
BeanUtil.copyProperties(userDTO,one,true);
save(one); //把copy之后的用户对象存储到数据库
}else {
throw new ServiceException(Constants.CODE_600,"用户已存在!");
}
return one;
}
private User getUserInfo(UserDTO userDTO){ //封装一个查询检测方法
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDTO.getUsername());
queryWrapper.eq("password",userDTO.getPassword());
User one;
// 第一种方式,后台查询出多条相同的账号
// List<User> list = list(queryWrapper);
// return list.size()!=0;
// 第二种方式
try{
one = getOne(queryWrapper); //从数据库查询用户信息
}catch (Exception e){
LOG.error(e);
throw new ServiceException(Constants.CODE_500,"系统错误");
}
return one;
}
7、在UserDTO中加上
package com.kep.springboot.controller.dto;
import lombok.Data;
/**
* 接收前端登录请求的参数
*/
@Data
public class UserDTO {
private String username;
private String password;
private String nickname;
private String email;
private String phone;
}
3、实现个人信息展示和修改界面
1、在views中创建Person.vue
<template>
<el-card style="width: 500px">
<el-form label-width="80px" size="small">
<el-form-item label="用户名">
<el-input v-model="form.username" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="form.address" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">确 定</el-button>
<el-button type="warning" @click="reBack" style="margin-left: 180px">返 回</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
name: "Person",
data(){
return{
form:{},
user:localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
}
},
created() {
this.request.get("/user/username/" + this.user.username).then(res => {
if (res.code === '200'){
this.form = res.data
}
})
},
methods:{
save(){
this.request.post("/user",this.form).then(res => {
if (res.data){
this.$message.success("保存成功!")
}else {
this.$message.error("保存失败!")
}
})
},
reBack(){
this.$router.push("/user")
}
}
}
</script>
<style scoped>
</style>
2、加上路由
{
path: '/',
component: () => import('../views/Manage.vue'),
redirect:'/home',
children:[
{path:'home', name:'首页', component: () =>import('../views/Home.vue')},
{path:'user',name:'用户管理',component: () => import('../views/User.vue')},
{path:'person',name:'个人信息',component: () => import('../views/Person.vue')}
]
},
3、在Header.vue中写上
<el-dropdown-item style="font-size: 14px;padding: 5px 0">
<span style="text-decoration: none" @click="personInfo">个人信息</span>
</el-dropdown-item>
personInfo(){
this.$router.push("/person")
}
4、将UserController.java中的接口返回值全部修改为Result
package com.kep.springboot.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kep.springboot.common.Constants;
import com.kep.springboot.common.Result;
import com.kep.springboot.controller.dto.UserDTO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import com.kep.springboot.service.IUserService;
import com.kep.springboot.entity.User;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* <p>
* 前端控制器
* </p>
*
* @author kp
* @since 2022-02-09
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
@PostMapping("/login")
public Result login(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)){
return Result.error(Constants.CODE_400,"参数错误");
}
UserDTO dto = userService.login(userDTO);
return Result.success(dto);
}
@PostMapping("/register")
public Result register(@RequestBody UserDTO userDTO){
String username = userDTO.getUsername();
String password = userDTO.getPassword();
String nickname = userDTO.getNickname();
String email = userDTO.getEmail();
String phone = userDTO.getPhone();
if (StrUtil.isBlank(username)||StrUtil.isBlank(password)||StrUtil.isBlank(nickname)||StrUtil.isBlank(email)||StrUtil.isBlank(phone)){
return Result.error(Constants.CODE_400,"参数错误");
}
return Result.success(userService.register(userDTO));
}
@PostMapping
public Result save(@RequestBody User user){
// 新增或者更新
return Result.success(userService.saveOrUpdate(user));
}
//删除
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
return Result.success(userService.removeById(id));
} /*user==user*/
//批量删除
@PostMapping("/del/batch")
public Result deleteBatch(@RequestBody List<Integer> ids) { //表示url参数
return Result.success(userService.removeBatchByIds(ids));
}
//查询
@GetMapping
public Result findAll() {
return Result.success(userService.list());
}
//根据id查询
@GetMapping("/{id}")
public Result findOne(@PathVariable Integer id) {
return Result.success(userService.getById(id));
}
@GetMapping("/username/{username}")
public Result findPerson(@PathVariable String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
return Result.success(userService.getOne(queryWrapper));
}
//分页查询
@GetMapping("/page")
public Result findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize,
@RequestParam(defaultValue = "") String username,
@RequestParam(defaultValue = "") String nickname,
@RequestParam(defaultValue = "") String address) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (!"".equals(username)) {
queryWrapper.like("username", username);
}
if (!"".equals(nickname)) {
queryWrapper.like("nickname", nickname);
}
if (!"".equals(address)) {
queryWrapper.like("address", address);
}
queryWrapper.orderByDesc("id");//新增数据倒叙排列
return Result.success(userService.page(new Page<>(pageNum, pageSize),queryWrapper));
}
/**
* 导出接口
*/
@GetMapping("/export")
public void export(HttpServletResponse response) throws Exception{
//从数据库查询出所有的数据
List<User> list = userService.list();
//通过工具类创建writer 写出到磁盘路径,我们就不导出到磁盘路径
//ExcelWriter writer = ExcelUtil.getWriter(fileUploadPath+"/用户信息.xlsx")
//在内存操作,写出到浏览器
ExcelWriter writer = ExcelUtil.getWriter(true);
//自定义标题别名,根据实体类User
writer.addHeaderAlias("username","用户名");
writer.addHeaderAlias("password","密码");
writer.addHeaderAlias("nickname","昵称");
writer.addHeaderAlias("email","邮箱");
writer.addHeaderAlias("phone","电话");
writer.addHeaderAlias("address","地址");
writer.addHeaderAlias("createTime","创建时间");
writer.addHeaderAlias("avatarUrl","头像");
//一次性写出list内的对象到excel,使用默认样式,强制输出标题
writer.write(list,true);
//设置浏览器响应的格式
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("用户信息","UTF-8");
response.setHeader("Content-Disposition","attachment;filename="+ fileName +".xlsx");
//通过response获取到输出流,把writer里面的东西刷新到输出流里面去,最后通过输出流返回到浏览器
ServletOutputStream out = response.getOutputStream();
writer.flush(out,true);
out.close();
writer.close();
}
/**
* excel 导入
* @param file
* @throws Exception
*/
@PostMapping("/import")
public Result imp(MultipartFile file) throws Exception{
InputStream inputStream =file.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
//方式一:通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来
// List<User> list = reader.readAll(User.class);
//方式二:忽略表头的中文,直接读取表的内容
List<List<Object>> list = reader.read(1);
List<User> users = CollUtil.newArrayList();
for (List<Object> row : list){
User user = new User();
user.setUsername(row.get(0).toString());
user.setPassword(row.get(1).toString());
user.setNickname(row.get(2).toString());
user.setEmail(row.get(3).toString());
user.setPhone(row.get(4).toString());
user.setAddress(row.get(5).toString());
users.add(user);
}
userService.saveBatch(users);
return Result.success(true);
}
}
5、最后将vue文件中所有res判断改为res.data,重启java部分便可成功📑
16、SpringBoot集成JWT
Jwt
全称是:json web token
。它将用户信息加密到token
里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token
的正确性,只要正确即通过验证。
一个token
分3部分,按顺序为
- 头部(
header
) - 载荷(
payload
) - 签证(
signature
)
优点
简洁: 可以通过URL、POST参数或者在HTTP header发送,因为数据量小,传输速度也很快;
自包含:负载中可以包含用户所需要的信息,避免了多次查询数据库;
因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持;
不需要在服务端保存会话信息,特别适用于分布式微服务。
缺点
无法作废已颁布的令牌;
不易应对数据过期。
1、项目依赖pom.xml
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2、JWT集成,记得要在UserDTO中设置参数token
package com.kep.springboot.utils;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
public class TokenUtils {
/**
* 生成token
* @return
*/
public static String genToken(String userId,String sign){
return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(),2)) //2小时后token过期
.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
}
}
token示例
{“username”:“admin”,“password”:“admin”,“nickname”:“管理员01”,“email”:“admin@qq.com”,“phone”:“1388888888”,“token”:“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwiZXhwIjoxNjQ0NzQwNDM0fQ.lEoUI7kS2I8qdm8WVDiwqVq3y8Wk9U7pX-6pAUPBAvU”}
3、在config文件下创建文件InterceptorConfig.java
package com.kep.springboot.config;
import com.kep.springboot.config.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**") // 拦截所有请求,通过判断token是否合法来决定是否需要登录
.excludePathPatterns("/user/login", "/user/register","/**/export","/**/import");
}
@Bean
public JwtInterceptor jwtInterceptor(){
return new JwtInterceptor();
}
}
4、创建文件夹interceptor,文件夹下创建JwtInterceptor.java
package com.kep.springboot.config.interceptor;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.kep.springboot.common.Constants;
import com.kep.springboot.entity.User;
import com.kep.springboot.exception.ServiceException;
import com.kep.springboot.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private IUserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("token");
//如果不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
//执行认证
if (StrUtil.isBlank(token)){
throw new ServiceException(Constants.CODE_401,"无token,请重新登录");
}
//获取 token 中的 user id
String userId;
try{
userId = JWT.decode(token).getAudience().get(0);
}catch (JWTDecodeException j){
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录");
}
//根据token中的userid查询数据库
User user = userService.getById(userId);
if (user == null){
throw new ServiceException(Constants.CODE_401,"用户不存在,请重新登录");
}
//用户密码加签验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try{
jwtVerifier.verify(token); //验证token
}catch (JWTVerificationException e){
throw new ServiceException(Constants.CODE_401,"token验证失败,请重新登录");
}
return true;
}
}
5、在utils中的request.js中加上
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
let user=localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : null
if (user){
config.headers['token'] = user.token; // 设置请求头
}
###后台获取用户信息
1、在UserController中加上
//获取当前用户信息
User currentUser = TokenUtils.getCurrentUser();
System.out.println("获取当前用户信息==============="+currentUser.getNickname());
2、在TokenUtils.java中加入
/**
* 获取当前登录的用户信息
* @return user对象
*/
public static User getCurrentUser() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)) {
String userId = JWT.decode(token).getAudience().get(0);
return staticUserService.getById(Integer.valueOf(userId));
}
}catch (Exception e) {
return null;
}
return null;
}
17、SpringBoot文件上传+用户信息修改同步
1、File.vue
<template>
<div>
<div style="padding: 10px 0">
<el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name" @keyup.enter.native="triggerClick"></el-input>
<el-button class="ml-5" type="primary" @click="load" ref="btn">搜索</el-button>
<el-button class="ml-5" type="warning" @click="reset">重置</el-button>
</div>
<div style="margin: 10px 0">
<el-upload action="http://localhost:9090/file/upload" :show-file-list="false" :on-success="handleFileUploadSuccess" style="display: inline-block">
<el-button type="primary" class="ml-5">上传文件 <i class="el-icon-top"></i> </el-button>
</el-upload>
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="您确定批量删除这些数据吗?"
@confirm="delBatch">
<el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i> </el-button>
</el-popconfirm>
</div>
<el-table :data="tableData" border stripe :header-cell-class-name="headerBg" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="文件名称"></el-table-column>
<el-table-column prop="type" label="文件类型"></el-table-column>
<el-table-column prop="size" label="文件大小(kb)"></el-table-column>
<el-table-column label="下载">
<template slot-scope="scope">
<el-button type="primary" @click="download(scope.row.url)">下载</el-button>
</template>
</el-table-column>
<el-table-column label="启用">
<template slot-scope="scope">
<el-switch v-model="scope.row.enable" active-color="#13ce66" inactive-color="#ccc" @change="changeEnable(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-popconfirm
class="ml-5"
confirm-button-text='确定'
cancel-button-text='取消'
icon="el-icon-info"
icon-color="red"
title="确定删除吗?"
@confirm="del(scope.row.id)">
<el-button type="danger" slot="reference" >删除 <i class="el-icon-remove-outline"></i></el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div style="padding: 10px 0">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination><!--分页栏-->
</div>
</div>
</template>
<script>
export default {
name: "File",
data(){
return {
tableData:[],
name:'',
multipleSelection:[],
headerBg:'headerBg',
pageNum:1,
pageSize:5,
total:0
}
},
created() {
this.load()
},
methods:{
load(){
this.request.get("/file/page",{
params:{
pageNum:this.pageNum,
pageSize:this.pageSize,
name:this.name,
}
}).then(res =>{
this.tableData = res.data.records
this.total = res.data.total
})
},
triggerClick(){ //用于回车键触发搜索功能
this.$refs.btn.$emit('click')
},
changeEnable(row){
this.request.post("/file/update",row).then(res =>{
if (res.code === '200'){
this.$message.success("操作成功!")
}
})
},
del(id){
this.request.delete("/file/"+id).then(res => {
if (res.code === '200'){
this.$message.success("删除成功!")
this.load()
}else {
this.$message.error("删除失败!")
}
})
},
handleSelectionChange(val){
console.log(val)
this.multipleSelection = val
},
delBatch(){
let ids = this.multipleSelection.map(v => v.id) //将多选获取的对象扁平化处理,即[{},{},{}] => [1,2,3]
this.request.post("/file/del/batch",ids).then(res => {
if (res.code === '200'){
this.$message.success("批量删除成功!")
this.load()
}else {
this.$message.error("批量删除失败!")
}
})
},
reset(){
this.name = ""
this.load()
},
handleSizeChange(pageSize){
console.log(pageSize)
this.pageSize=pageSize
this.load()
},
handleCurrentChange(pageNum){
console.log(pageNum)
this.pageNum=pageNum
this.load()
},
handleFileUploadSuccess(res){
console.log(res)
this.load()
},
download(url){
window.open(url)
}
}
}
</script>
<style scoped>
</style>
Person.vue
<template>
<el-card style="width: 500px">
<el-form label-width="80px" size="small">
<el-upload
action="https://localhost:9090/file/upload"
:show-file-list="false">
</el-upload>
<el-form-item label="用户名">
<el-input v-model="form.username" disabled autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="form.email" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="电话">
<el-input v-model="form.phone" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input type="textarea" v-model="form.address" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">确 定</el-button>
<el-button type="warning" @click="reBack" style="margin-left: 180px">返 回</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
export default {
name: "Person",
data(){
return{
form:{},
user:localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
}
},
created() {
this.getUser().then(res => {
console.log(res)
this.form = res
})
},
methods:{
async getUser(){
return (await this.request.get("/user/username/"+this.user.username)).data
},
save(){
this.request.post("/user",this.form).then(res => {
if (res.code === '200'){
this.$message.success("保存成功!")
//触发父级更新User的方法
this.$emit("refreshUser")
//更新浏览器存储用户信息
this.getUser().then(res => {
res.token = JSON.parse(localStorage.getItem("user")).token
localStorage.setItem("user",JSON.stringify(res))
})
}else {
this.$message.error("保存失败!")
}
})
},
reBack(){
this.$router.push("/user")
}
}
}
</script>
<style scoped>
</style>
3、router中的index.js
const routes = [
{
path: '/',
component: () => import('../views/Manage.vue'),
redirect:'/home',
children:[
{path:'home', name:'首页', component: () =>import('../views/Home.vue')},
{path:'user',name:'用户管理',component: () => import('../views/User.vue')},
{path:'person',name:'个人信息',component: () => import('../views/Person.vue')},
{path:'file',name:'文件管理',component: () => import('../views/File.vue')},
]
},
4、Manage.vue
<template>
<el-container style="min-height:100vh /*border: 1px solid #eee*/">
<el-aside width="sideWidth + 'px'" style="background-color: rgb(238, 241, 246);box-shadow: 1px 0 6px rgb(205 133 63)">
<Aside :isCollapse="isCollapse" :logoTextShow="logoTextShow"/> <!--给组件传值-->
</el-aside>
<el-container>
<el-header style="border-bottom: 1px solid #ccc">
<Header :collapse-btn-class="collapseBtnClass" v-on:click="collapse" :user="user"/>
</el-header>
<el-main>
<!-- 表示当前页面的子路由会在 <router-view /> 里面显示-->
<router-view @refreshUser="getUser"/>
</el-main>
</el-container>
</el-container>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
// import request from "../utils/request";
import Aside from "../components/Aside";
import Header from "../components/Header";
export default {
name: 'Home',
data(){
return {
collapseBtnClass:'el-icon-s-fold',
isCollapse:false,
sideWidth:200,
logoTextShow:true,
user:{}
}
},
components:{
Header,
Aside
},
created() {
//从后台获取最新的User数据
this.getUser()
},
methods:{
collapse(){ //点击收缩按钮触发
this.isCollapse = !this.isCollapse
if (this.isCollapse){
this.sideWidth = 64
this.collapseBtnClass='el-icon-s-unfold'
this.logoTextShow = false
}else{ //展开
this.sideWidth = 200
this.collapseBtnClass = 'el-icon-s-fold'
this.logoTextShow = true
}
},
getUser() {
let username =localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")).username : ""
//从后台获取user数据
this.request.get("/user/username/" + username).then(res => {
//重新赋值后台的最新User数据
this.user=res.data
})
}
}
}
</script>
5、实体类File.java
package com.kep.springboot.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("sys_file")
public class Files {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private String type;
private Long size;
private String url;
private String md5;
private Boolean isDelete;
private Boolean enable;
}
6、FileMapper.java
package com.kep.springboot.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kep.springboot.entity.Files;
public interface FileMapper extends BaseMapper<Files> {
}
7、application.yml
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dangjian?serverTimezone=GMT%2b8
username: root
password: 180918
mybatis:
mapper-locations: classpath:mapper/*.xml #扫描所有mybatis的xml文件
#configuration:
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
files:
upload:
path: D:/Intellij IDEA/files/
18、整合Echarts
下载Echarts:terminal中npm i echarts -S
在主页中引入echarts:import * as echarts from 'echarts'
官网:https://echarts.apache.org/zh/index.html
使用:
1、折线图
2、柱状图
3、饼图
示例:Home.vue
<template>
<div>
<div id="main" style="width: 500px;height: 400px"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
name: "Home",
data(){
return{
}
},
mounted() { //页面元素渲染之后再触发
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
let base = +new Date(1968, 9, 3);
let oneDay = 24 * 3600 * 1000;
let date = [];
let data = [Math.random() * 300];
for (let i = 1; i < 20000; i++) {
var now = new Date((base += oneDay));
date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1]));
}
option = {
tooltip: {
trigger: 'axis',
position: function (pt) {
return [pt[0], '10%'];
}
},
title: {
left: 'center',
text: 'Large Area Chart'
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: date
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%']
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 10
},
{
start: 0,
end: 10
}
],
series: [
{
name: 'Fake Data',
type: 'line',
symbol: 'none',
sampling: 'lttb',
itemStyle: {
color: 'rgb(255, 70, 131)'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(255, 158, 68)'
},
{
offset: 1,
color: 'rgb(255, 70, 131)'
}
])
},
data: data
}
]
};
option && myChart.setOption(option);
}
}
</script>
<style scoped>
</style>