提示:这里是通过vue3集成elementplus框架的提升,加强大家对文档阅读的能力,冲冲冲!!!
<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>
<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视图
<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>
<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>
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();
}
}
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;
}
}
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
<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>
这里的话是用到了文件存储,有兴趣的同学可以使用第三方的也很不错的哦
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\\");
}
}
<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>
<template>
路径导航
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
<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>
此时你会发现图片加载可能失败。【原因是因为你的用户信息数据表的头像路径不能映射到后端的路径中】
提示:本小节的主体内容为搭建后台的框架内容,动态的加载左侧的导航栏信息以及其他各个部位的信息填充等!!!
本章的第六小节完毕,敬请期待后续更新(可留言需要学习哪方面的内容哈)!如果需要源码或者工具的朋友们可关注微信公众号"锅锅编程生活"或者扫描二维码关注回复关键字/后台留言获取即可!