我们在上一章的基础上进行学习。
https://blog.51cto.com/u_13312531/6934943
我们这一章实现的效果如图所示:
顶部显示用户信息和退出登录按钮,左边显示导航列表,右侧显示主体内容。
一、修改vue的main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
// 导入axios
import axios from 'axios'
// 挂载axios
Vue.prototype.$http = axios
// 设置访问根路径
axios.defaults.baseURL = "http://localhost:9000"
// 导入elementui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
import './assets/css/global.css'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: {App},
template: '<App/>'
})
const originaPush = VueRouter.prototype.push
VueRouter.prototype.push=function push(location,onResolve,onReject){
if (onResolve || onReject) return originaPush.call(this,location,onResolve,onReject)
return originaPush.call(this,location).catch(err=>err);
}
// 挂载路由导航
router.beforeEach((to,from,next)=>{
// to将要访问
// from 从哪访问
// next 接着重定向到url上
if(to.path=='/login') return next();
// 取出当前用户
const userFlag = window.sessionStorage.getItem("user");
// 没有用户数据就跳转到登录页面
if (!userFlag) return next('/login');
next();//符合要求 放行
})
export default router
二、修改Home页面
<template>
<div>
<el-container class="home_container">
<el-header>
<span>{{ msg }}</span>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
<el-container>
<!-- 侧边栏 -->
<el-aside width="250px">
<div class="toggle-button" @click="toggleCollapase">|||</div>
<el-menu background-color="#545c64" text-color="#fff" active-text-color="#409eff" :default-active="activePath"
:router="true" :collapse-transition="false"
unique-opened :collapse="isCollapse">
<el-submenu :index="item.id+''" v-for="item in menuList" :key="item.id">
<!-- 一级菜单 -->
<template slot="title">
<i class="el-icon-location">
<span>{{ item.title }}</span>
</i>
</template>
<!-- 二级菜单 -->
<el-menu-item :index="it.path+''" v-for="it in item.childMenus" :key="it.id"
@click="saveNavState(it.path)">
<template slot="title">
<i class="el-icon-location" style="margin-left: 20px">
<span>{{ it.title }}</span>
</i>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 主体内容 -->
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: 'Home',
data() {
return {
msg: 'welcome:' + window.sessionStorage.getItem("user"),
// 导航菜单列表
menuList: [],
// 是否伸缩
isCollapse: false,
// 当前访问导航页面
activePath: '/welcome'
}
},
created() {
// 获取导航列表
this.getMenuList();
// 取出session中path,动态设置最后点击导航的路径
this.activePath = window.sessionStorage.getItem("activePath");
},
methods: {
// 退出
logout() {
this.$router.push("/login");
// 清空session
window.sessionStorage.removeItem("user");
},
// 获取导航列表
async getMenuList() {
const {data: res} = await this.$http.get("menus");
console.log(res);
if (res.code !== 0) {
return this.$message.console.error("获取列表失败")
}
this.menuList = res.menus;
},
// 控件伸缩
toggleCollapase() {
this.isCollapse = !this.isCollapse
},
// 导航点击
saveNavState(activePath) {
// 保存导航的路径到session
window.sessionStorage.setItem("activePath", activePath);
this.activePath = activePath;
}
}
}
</script>
<style scoped>
.home_container {
height: 100%;
}
.el-header {
background-color: rgb(44, 58, 58);
display: flex;
justify-content: space-between;
padding-left: 0;
color: beige;
font-size: 20px;
.div {
display: flex;
align-items: center;
span {
margin-left: 15px;
}
}
}
.el-aside {
background-color: rgb(13, 20, 18);
.el-menu {
border-right: none;
}
}
// 伸缩按钮样式
.toggle-button {
background-color: #4a5064;
font-size: 10px;
line-height: 24px;
color: #fff;
text-align: center;
letter-spacing: 0.2em;
cursor: pointer;
}
.el-main {
background-color: beige;
}
</style>
页面的主要功能是当进入页面的时候请求导航列表数据,并显示导航的一级导航和二级导航,并在点击导航的时候保存当前导航的路径,当刷新页面后仍保留到当前页面。
三、后端导航列表实现
1.创建导航实体类
package com.example.yonfu.bean;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.util.List;
/**
* @author qx
* @date 2023/8/2
* @des 导航实体类
*/
@Data
@Entity
@Table(name = "t_menus")
@Accessors(chain = true)
public class Menus {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 菜单名称
*/
private String title;
/**
* 父级菜单ID
*/
private Long parentId;
/**
* 路径地址
*/
private String path;
/**
* 子级导航列表
*/
@Transient
private List<Menus> childMenus;
}
2.数据持久层
package com.example.yonfu.repository;
import com.example.yonfu.bean.Menus;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author qx
* @date 2023/8/2
* @des 导航持久类
*/
public interface MenusRepository extends JpaRepository<Menus, Long> {
}
3.导航业务层
主要生成导航的树形列表数据
package com.example.yonfu.service;
import com.example.yonfu.bean.Menus;
import com.example.yonfu.repository.MenusRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2023/8/2
* @des 导航业务层
*/
@Service
@RequiredArgsConstructor
public class MenusService {
private final MenusRepository menusRepository;
/**
* 获取导航树形列表结果
*
* @return 树形列表
*/
public List<Menus> getData() {
// 所有导航列表数据
List<Menus> parentMenus = menusRepository.findAll();
if (!CollectionUtils.isEmpty(parentMenus)) {
List<Menus> resultList = new ArrayList<>();
//获取顶层元素集合
Long parentCode;
for (Menus entity : parentMenus) {
parentCode = entity.getParentId();
//顶层元素的parentCode==null或者为0
if (parentCode == 0) {
resultList.add(entity);
}
}
//获取每个顶层元素的子数据集合
for (Menus entity : resultList) {
entity.setChildMenus(getSubList(entity.getId(), parentMenus));
}
return resultList;
}
return parentMenus;
}
/**
* 获取子数据集合
*
* @param id
* @param entityList
*/
private static List<Menus> getSubList(Long id, List<Menus> entityList) {
List<Menus> children = new ArrayList<>();
Long parentId;
//子集的直接子对象
for (Menus entity : entityList) {
parentId = entity.getParentId();
if (id.equals(parentId)) {
children.add(entity);
}
}
//子集的间接子对象
for (Menus entity : children) {
entity.setChildMenus(getSubList(entity.getId(), entityList));
}
//递归退出条件
if (children.size() == 0) {
return null;
}
return children;
}
}
4.控制层完善
package com.example.yonfu.controller;
import com.example.yonfu.bean.User;
import com.example.yonfu.service.MenusService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @author qx
* @date 2023/8/2
* @des 控制层
*/
@RestController
public class TestController {
@Autowired
private MenusService menusService;
/**
* 登录
*
* @param user 用户请求对象
* @return 登录结果
*/
@PostMapping("/login")
public Map<String, Object> test(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
// 为了测试方便 就在控制层中写业务逻辑了
if ("admin".equals(user.getUsername()) && "123".equals(user.getPassword())) {
map.put("code", 0);
map.put("msg", "登录成功");
} else {
map.put("code", 1);
map.put("msg", "登录失败");
}
return map;
}
/**
* 导航列表
*
* @return
*/
@GetMapping("/menus")
public Map<String, Object> getMenusData() {
Map<String, Object> map = new HashMap<>();
map.put("code", 0);
map.put("menus", menusService.getData());
return map;
}
}
导航菜单的测试数据如下所示:
四、页面主体内容的实现
1.创建几个新页面
Welcome.vue:
<script>
export default {
name: 'Welcome'
}
</script>
<template>
<div>
<h1>开始学习</h1>
</div>
</template>
<style scoped>
</style>
Springboot.vue:
这个页面主要实现职工列表的显示、分页和关键字查询等功能。
<template>
<div>
<!--面包屑 -->
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">springboot</a></el-breadcrumb-item>
</el-breadcrumb>
<!--主体 -->
<el-card>
<el-row :gutter="25">
<el-col :span="10">
<el-input placeholder="请输入搜索的用户名" v-model="queryInfo.username">
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
</el-col>
<!-- <el-col :span="4">
<el-button type="primary" @click="search">搜索</el-button>
</el-col>-->
</el-row>
<!-- 用户列表 -->
<el-table :data="employeeList" border stripe>
<el-table-column type="index"></el-table-column>
<el-table-column label="用户名" prop="username"></el-table-column>
<el-table-column label="邮箱" prop="email"></el-table-column>
<el-table-column label="角色" prop="role"></el-table-column>
<el-table-column label="状态" prop="state">
<template slot-scope="scope">
<el-switch v-model="scope.row.state" :active-value="0" :inactive-value="1"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini"></el-button>
<el-button type="danger" icon="el-icon-delete" size="mini"></el-button>
<el-tooltip effect="dark" content="分配权限" placement="top-start" :enterable="false">
<el-button type="warning" icon="el-icon-setting" size="mini"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div>
<!--分页插件-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pageNum"
:page-sizes="[1,2,5,100]"
:page-size="queryInfo.pageSize"
layout="total,sizes,prev,pager,next,jumper"
:total="total">
</el-pagination>
</div>
</el-card>
</div>
</template>
<script>
export default {
name: 'Springboot',
data() {
return {
// 查询信息实体
queryInfo: {
username: '', //用户名
pageNum: 1, // 页码
pageSize: 2 //每页条数
},
employeeList: [], //职工列表
total: 0 // 职工总数
}
},
created() {
// 职工列表请求
this.getUserList()
},
methods: {
// 职工列表请求
async getUserList() {
const {data: res} = await this.$http.get("employee/list", {params: this.queryInfo});
console.log(res);
this.total = res.total
this.employeeList = res.data
},
// 修改每页条数触发的事件
handleSizeChange(newSize) {
this.queryInfo.pageSize = newSize
this.getUserList();
},
// 修改当前页码触发的事件
handleCurrentChange(newPage) {
this.queryInfo.pageNum = newPage
this.getUserList()
},
// 关键词搜索
search() {
// 从第一页开始查询
this.queryInfo.pageNum = 1
this.getUserList()
}
}
}
</script>
<style scoped>
.el-breadcrumb {
margin-bottom: 15px;
font-size: 12px;
}
.el-table {
margin-top: 10px;
}
.el-card {
box-shadow: 0 1px 1px rgb(0, 8, 10, 0.15) !important;
}
</style>
Springcloud.vue:
<script >
export default {
name: 'Springcloud'
}
</script>
<template>
<div>
test Springcloud
</div>
</template>
<style scoped>
</style>
2.修改路由配置文件
把前面新建的几个文件配置到路由中
import Vue from 'vue'
import Router from 'vue-router'
import Login from "../components/Login.vue";
import Home from "../components/Home.vue";
import Welcome from "../components/Welcome.vue";
import Springboot from "../components/Springboot.vue";
import Springcloud from "../components/Springcloud.vue";
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/home',
name: 'Home',
component: Home,
redirect:'/welcome',
children:[
{path:'/welcome',component:Welcome},
{path:'/springboot',component:Springboot},
{path:'/springcloud',component:Springcloud}
]
}
]
})
五、后端职工功能实现
1.创建职工实体类
package com.example.yonfu.bean;
import lombok.Data;
import javax.persistence.*;
/**
* @author qx
* @date 2023/8/2
* @des 职工实体类
*/
@Data
@Entity
@Table(name = "t_employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 用户名
*/
private String username;
/**
* 邮箱
*/
private String email;
/**
* 密码
*/
private String password;
/**
* 角色
*/
private String role;
// 状态 0:正常 1:禁用
@Column(columnDefinition = "tinyint(1) default 0 comment '状态'")
private Integer state;
}
2.创建职工数据持久层
package com.example.yonfu.repository;
import com.example.yonfu.bean.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* @author qx
* @date 2023/8/2
* @des 职工数据持久层
*/
public interface EmployeeRepository extends JpaRepository<Employee, Long>, JpaSpecificationExecutor<Employee> {
}
3.创建职工业务层实现
package com.example.yonfu.service;
import com.example.yonfu.bean.Employee;
import com.example.yonfu.repository.EmployeeRepository;
import com.example.yonfu.request.EmployeeRequest;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author qx
* @date 2023/8/2
* @des 职工业务层
*/
@Service
@RequiredArgsConstructor
public class EmployeeService {
private final EmployeeRepository employeeRepository;
/**
* 职工列表
*
* @param employee 职工请求对象
* @return 职工列表和总数数据
*/
public Map<String, Object> getEmployeeList(EmployeeRequest employee) {
Specification<Employee> specification = (root, query, cb) -> {
List<Predicate> predicateList = new ArrayList<>();
// 用户名关键字查询
if (StringUtils.isNotBlank(employee.getUsername())) {
predicateList.add(cb.like(root.get("username").as(String.class), "%" + employee.getUsername() + "%"));
}
Predicate[] predicates = new Predicate[predicateList.size()];
return cb.and(predicateList.toArray(predicates));
};
Page<Employee> employeePage = employeeRepository.findAll(specification, PageRequest.of(employee.getPageNum() - 1, employee.getPageSize()));
Map<String, Object> map = new HashMap<>();
// 职工列表
map.put("total", employeePage.getTotalElements());
// 职工总数
map.put("data", employeePage.getContent());
return map;
}
}
4.创建职工请求实体类
package com.example.yonfu.request;
import lombok.Data;
/**
* @author qx
* @date 2023/8/2
* @des 员工请求类
*/
@Data
public class EmployeeRequest {
/**
* 用户名
*/
private String username;
/**
* 当前页码
*/
private Integer pageNum;
/**
* 每页条数
*/
private Integer pageSize;
}
5.创建职工控制层测试
package com.example.yonfu.controller;
import com.example.yonfu.request.EmployeeRequest;
import com.example.yonfu.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* @author qx
* @date 2023/8/2
* @des 职工控制层
*/
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/**
* 职工列表
*
* @param employeeRequest
* @return
*/
@GetMapping("/list")
public Map<String, Object> getEmployeeList(EmployeeRequest employeeRequest) {
return employeeService.getEmployeeList(employeeRequest);
}
}
6.测试
我们在职工表添加一些测试数据
启动后端项目和前端项目后,点击导航切换页面跳转到不同的页面。
在职工列表页面通过关键字查询可以获取到我们希望的职工列表数据。
到这里我们基本实现了SpringBoot和Vue的使用。这里只作为一个学习和记录的目的。