SpringBoot+SpringSecurity+MybatisPlus+Vue3小项目摘录(六)
2023-12-31 08:28:44
后台主界面搭建
提示:这里是通过vue3集成elementplus框架的提升,加强大家对文档阅读的能力,冲冲冲!!!
页面可以直接参考elementPlus中的布局容器实现
编写index.vue
html代码
<div class="app-wrapper">
<el-container>
<el-aside width="200px" class="sidebar-container"><Menu/></el-aside>
<el-container>
<el-header><Header/></el-header>
<el-main><Tabs/></el-main>
<el-footer><Footer/></el-footer>
</el-container>
</el-container>
</div>
css样式代码
<style scoped>
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.sidebar-container {
background-color: #2d3a4b;
height: 100%;
}
.el-container{
height:100%
}
.el-header{
padding-left: 0px;
padding-right: 0px;
}
:deep(ul.el-menu){
border-right-width: 0px
}
</style>
预览效果如下:
在页面中为了更好的区分每个位置的布局以及内容,则在layout目录下分别新建四个目录存放不同的vue视图
每个vue视图中的内容都是相同的
在lauout/index.vue中进行引入组件
tabs/index.vue组件的内容如下:
<template>
<div style="margin-bottom: 20px">
<el-button size="small" @click="addTab(editableTabsValue)">
add tab
</el-button>
</div>
<el-tabs
v-model="editableTabsValue"
type="card"
class="demo-tabs"
closable
@tab-remove="removeTab"
>
<el-tab-pane
v-for="item in editableTabs"
:key="item.name"
:label="item.title"
:name="item.name"
>
{{ item.content }}
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import { ref } from 'vue'
let tabIndex = 2
const editableTabsValue = ref('2')
const editableTabs = ref([
{
title: 'Tab 1',
name: '1',
content: 'Tab 1 content',
},
{
title: 'Tab 2',
name: '2',
content: 'Tab 2 content',
},
])
const addTab = (targetName) => {
const newTabName = `${++tabIndex}`
editableTabs.value.push({
title: 'New Tab',
name: newTabName,
content: 'New Tab content',
})
editableTabsValue.value = newTabName
}
const removeTab = (targetName) => {
const tabs = editableTabs.value
let activeName = editableTabsValue.value
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
editableTabsValue.value = activeName
editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
}
</script>
<style>
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
</style>
footer/index.vue组件的内容如下:
<template>
<div class="footer">
Copyright ? 2023-2024 许锅锅在线Java学习 版权所有 <a href="https://blog.csdn.net/xu772710255/article/details/135202635?spm=1001.2014.3001.5501" target="_blank">锅锅编程生活</a>
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.footer{
padding: 20px;
display: flex;
align-items: center;
}
</style>
左侧动态权限菜单的实现
1、登录成功之后在后台动态的查询该用户的权限信息
修改LoginSuccessHandler登录成功的处理器
package com.xuguoguo.hanlder;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xuguoguo.common.Result;
import com.xuguoguo.entity.SysMenu;
import com.xuguoguo.entity.SysRole;
import com.xuguoguo.entity.SysUser;
import com.xuguoguo.service.SysMenuService;
import com.xuguoguo.service.SysRoleService;
import com.xuguoguo.service.SysUserService;
import com.xuguoguo.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
@Package: com.xuguoguo.hanlder
@ClassName: LoginSuccessHandler
@Author: XuGuoGuo
@CreateTime: 2023/12/11-20:08
@Description:
*/
@Component
@Slf4j
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private SysMenuService sysMenuService;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Authentication authentication) throws IOException, ServletException {
log.info("正在执行到【登录校验】--->成功<---的处理器中……");
httpServletResponse.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
// String username="user";
String username=authentication.getName();
String token = JwtUtils.createJWT(username);
//根据用户名查询当前的用户信息
SysUser currentUser = sysUserService.getByUsername(username);
// 根据用户id获取所有的角色信息
List<SysRole> roleList = sysRoleService.list(new QueryWrapper<SysRole>().
inSql("id", "SELECT role_id FROM sys_user_role WHERE user_id=" + currentUser.getId()));
// 遍历所有的角色,获取所有菜单权限 而且不重复
Set<SysMenu> menuSet=new HashSet<>();
for(SysRole sysRole:roleList){
List<SysMenu> sysMenuList = sysMenuService.list(new QueryWrapper<SysMenu>().
inSql("id", "SELECT menu_id FROM sys_role_menu WHERE role_id=" + sysRole.getId()));
for(SysMenu sysMenu:sysMenuList){
menuSet.add(sysMenu);
}
}
List<SysMenu> sysMenuList=new ArrayList<>(menuSet);
// 排序
sysMenuList.sort(Comparator.comparing(SysMenu::getOrderNum));
// 转菜单树
List<SysMenu> menuList=sysMenuService.buildTreeMenu(sysMenuList);
outputStream.write(JSONUtil.toJsonStr(Result.ok("登录成功").put("authorization",token)
.put("code",200).
put("currentUser",currentUser).put("menuList",menuList))
.getBytes());
outputStream.flush();
outputStream.close();
}
}
在SysMenu实体对象中添加属性【存放子节点的数据】
业务层处理菜单树的数据处理
public interface SysMenuService extends IService<SysMenu> {
List<SysMenu> buildTreeMenu(List<SysMenu> sysMenuList);
}
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu>
implements SysMenuService{
@Override
public List<SysMenu> buildTreeMenu(List<SysMenu> sysMenuList) {
List<SysMenu> resultMenuList=new ArrayList<>();
for(SysMenu sysMenu:sysMenuList){
// 寻找子节点
for(SysMenu e:sysMenuList){
if(e.getParentId()==sysMenu.getId()){
sysMenu.getChildren().add(e);
}
}
//如果为0则表示父节点
if(sysMenu.getParentId()==0L){
resultMenuList.add(sysMenu);
}
}
return resultMenuList;
}
}
前端stroe目录下的index.js需要修改存取数据
import { createStore } from 'vuex'
export default createStore({
state: {
},
getters: {
GET_TOKEN:state => {
return sessionStorage.getItem("token")
},
GET_MENULIST:state => {
return JSON.parse(sessionStorage.getItem("menuList"));
}
},
mutations: {
SET_TOKEN:(state,token)=>{
sessionStorage.setItem("token",token);
},
SET_MENULIST:(state,menuList)=>{
sessionStorage.setItem("menuList",JSON.stringify(menuList));
}
},
actions: {
},
modules: {
}
})
在登录成功的逻辑中处理菜单数据加载
重新登录查看用户的信息
左侧菜单组件加载
参考elementplus官网的菜单组件即可
https://element-plus.gitee.io/zh-CN/component/menu.html#%E4%BE%A7%E6%A0%8F
修改menu/index.vue组件内容
<template>
<el-menu
active-text-color="#ffd04b"
background-color="#2d3a4b"
class="el-menu-vertical-demo"
text-color="#fff"
router
:default-active="'/index'"
>
<el-menu-item index="/index">
<el-icon><home-filled /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu :index="menu.path" v-for="menu in menuList" :key="menu.id">
<template #title>
<el-icon><svg-icon :icon="menu.icon"/></el-icon>
<span>{{ menu.name }}</span>
</template>
<el-menu-item :index="item.path" v-for="item in menu.children" :key="item.id">
<el-icon><svg-icon :icon="item.icon"/></el-icon>
<span>{{ item.name }}</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script setup>
import {HomeFilled} from '@element-plus/icons-vue'
import {ref} from 'vue'
import store from '@/store'
const menuList=ref(store.getters.GET_MENULIST);
</script>
<style lang="scss" scoped>
</style>
预览效果如下:
后台系统右上角用户信息的渲染
- 目前我们放在电脑的某个目录中【用户头像==userAvatar》
这里的话是用到了文件存储,有兴趣的同学可以使用第三方的也很不错的哦
1、阿里云oss
2、minio
3、七牛云
【有兴趣不会的可以咨询我哦!~~~】
在后端中的配置类中添加请求资源和图片映射的资源路径
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/image/userAvatar/**").
addResourceLocations("file:D:\\photo\\userAvatar\\");
}
}
前端中为了获取用户信息需要修改store/index.js文件
在Login.vue登录成功的逻辑中获取用户信息
header头部分为左右两部分【左边为路径导航,右边显示用户信息】
在header下新建目录components目录
avatar.vue组件内容如下:
<template>
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar shape="square" :size="40" :src="squareUrl" />
{{currentUser.username}}
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item @click="logout">安全退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup>
import { ArrowDown } from '@element-plus/icons-vue'
import {ref} from 'vue'
import store from '@/store'
import requestUtil,{getServerUrl} from '@/util/request'
const currentUser=ref(store.getters.GET_USERINFO);
const squareUrl=ref(getServerUrl()+'image/userAvatar/'+currentUser.value.avatar)
const logout=async ()=>{
let result=await requestUtil.get("/logout")
if(result.data.code==200){
store.dispatch('logout')
}
}
</script>
<style lang="scss" scoped>
.el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
}
</style>
breadcrumb.vue组件内容如下:
<template>
路径导航
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
header/index.vue的视图如下:
<template>
<div class="navbar">
<Breadcrumb/>
<div class="navbar-right">
<Avatar/>
</div>
</div>
</template>
<script setup>
import Breadcrumb from './components/breadcrumb.vue'
import Avatar from './components/avatar.vue'
</script>
<style lang="scss" scoped>
.navbar {
width: 100%;
height: 60px;
overflow: hidden;
background-color: #F5F5F5;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
padding: 0 16px;
display: flex;
align-items: center;
box-sizing: border-box;
position: relative;
.navbar-right {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
:deep(.navbar-item) {
display: inline-block;
margin-left: 18px;
font-size: 22px;
color: #5a5e66;
box-sizing: border-box;
cursor: pointer;
}
}
}
</style>
预览效果如下:
此时你会发现图片加载可能失败。【原因是因为你的用户信息数据表的头像路径不能映射到后端的路径中】
打开用户信息数据表的avatar的列中值
打开存储头像的目录路径
重新登录后显示图片
用户注销的实现
在avatar.vue组件中的安全退出上添加事件
修改stroe目录下的index.js文件
后端的注销的处理器对象MyLogoutSuccessHandler中
小结
提示:本小节的主体内容为搭建后台的框架内容,动态的加载左侧的导航栏信息以及其他各个部位的信息填充等!!!
本章的第六小节完毕,敬请期待后续更新(可留言需要学习哪方面的内容哈)!如果需要源码或者工具的朋友们可关注微信公众号"锅锅编程生活"或者扫描二维码关注回复关键字/后台留言获取即可!
文章来源:https://blog.csdn.net/xu772710255/article/details/135282910
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!