当前位置: 首页 > news >正文

17、springboot3 vue3开发平台-前端-主页面布局搭建

文章目录

  • 1. 修改App.vue
  • 2. 布局页面
    • 2.1 src\views\layout\Layout.vue
    • 2.2 src\views\layout\Aside.vue
    • 2.3 src\views\layout\Header.vue
    • 2.4 src\views\layout\MainView.vue
    • 2.5 src\views\layout\Footer.vue

1. 修改App.vue

项目采用vue router 来管理路由, 作为单页面应用, 其页面为vue组件,通过路由动态渲染,App.vue作为根组件,需要配置路由出口
在这里插入图片描述

2. 布局页面

2.1 src\views\layout\Layout.vue


<template><el-container style="height: 100vh"><el-aside width="auto"><el-scrollbar><LeftLayout :isCollapse='isCollapse' /></el-scrollbar></el-aside><el-container><el-header style="height: 5vh; padding: 0 5px 0 5px"><Header @parentClick='parentClick' /></el-header><el-main style="padding: 0;"><MainView /></el-main><el-footer style="height: 3vh; padding: 0;"><Footer/></el-footer></el-container></el-container>
</template><script setup lang="ts">
import LeftLayout from './Aside.vue'
import Header from './Header.vue'
import MainView from './MainView.vue'
import Footer from './Footer.vue'import { ref } from 'vue'
const isCollapse = ref<any>(false)
const parentClick = (isCollapseValue: any) => {isCollapse.value = isCollapseValue
}
</script><style lang="scss" scoped>
</style>

2.2 src\views\layout\Aside.vue

使用前边自定义菜单组件

<template><div class="menu-container"><div class="up-title" v-if="!isCollapse"><!-- <div><svg-icon name="" width="20px" height="20px" /></div> --><div class="title-text">{{ !isCollapse ? "Vue3 boot3 快速开发平台" : "" }}</div></div><div class="down-menu"><el-menu :default-active="route.path" class="el-menu-vertical-demo" :collapse="isCollapse" :router="true" :collapse-transition="false"><MenuTree :menuList="menuList"></MenuTree></el-menu></div></div>
</template><script setup  lang="ts">
import { onMounted} from 'vue'
//渲染菜单的组件
import MenuTree from "@/components/menuTree/index.vue"
import { useMenuStore } from '@/stores/menu.js'
import { useRoute } from 'vue-router'const menuStore = useMenuStore()
const route = useRoute()// 获取pinia的缓存的菜单数据
const menuList = menuStore.menuListconst props = defineProps({isCollapse: Boolean
})</script><style lang="scss">
.menu-container {display: flex;flex-direction: column;height: 100vh;.up-title {display: flex;flex-direction: row;justify-content: center;align-items: center;height: 5vh;// padding: 3px;/* 内边距 */border-bottom: 2px solid;border-right: 1px solid;/* 设置下边框宽度和样式 */border-color: #f5f5f5;.title-text {display: inline-block;vertical-align: middle;/* 文字与图片垂直居中对齐 */font-weight: bold;/* 加粗文字 */font-size: 13px;margin-left: 5px;}}.down-menu {flex-grow: 1;}
}.el-menu-vertical-demo {height: 100%;overflow: auto;
}
</style>

2.3 src\views\layout\Header.vue

<template><div class="header-container"><!-- div left --><div class="left"><!-- 折叠按钮--><div class="div-item"  @click="toggleCollapse()"><el-icon :size="22" v-show="!isMenuOpen"><Fold /></el-icon><el-icon :size="22" v-show="isMenuOpen"><Expand /></el-icon></div><!-- 面包屑 --><div class="div-item"><el-breadcrumb separator="/"><el-breadcrumb-item :to="{ path: '/' }"></el-breadcrumb-item><template v-for="(item, index) in breadList"><el-breadcrumb-item v-if="item.menuName" :key="index" >{{ item.menuName }}</el-breadcrumb-item></template></el-breadcrumb></div></div><!-- div right --><div class="right"><div><el-icon size="20" @click="fullScreenHander"><FullScreen /></el-icon>   </div><div><!-- <el-avatar> {{ userData.username }} </el-avatar> --></div><div><span style="font-size: 18px;">{{ userData.username }}</span></div><div><el-dropdown><!-- <el-icon size="24"><MoreFilled /></el-icon> --><el-icon size="20"><i-ep-User /></el-icon><template #dropdown><el-dropdown-menu><el-dropdown-item  @click="selfInfoHander"><el-icon><UserFilled /></el-icon>个人信息</el-dropdown-item><el-dropdown-item  @click="exitHander"><el-icon><ArrowLeft /></el-icon>退出登录</el-dropdown-item></el-dropdown-menu></template></el-dropdown></div></div></div>
</template><script setup lang="ts">
import { Fold, Expand, FullScreen, ArrowDown, ArrowLeft, UserFilled } from '@element-plus/icons-vue';import { ref } from 'vue';
import { useRoute } from 'vue-router'import {loginOutService } from '@/api/auth/index'// 从stors中获取tab数据
import { useTokenStore } from "@/stores/token"
const tokenStore = useTokenStore()
import { useRouter } from 'vue-router';
const router = useRouter()import { useMenuStore } from '@/stores/menu'
import { storeToRefs } from 'pinia';
const menuStore = useMenuStore();
let { breadList } = storeToRefs(menuStore)
const route = useRoute();import {useUserInfoStore } from '@/stores/userInfo';
const userInfoStore = useUserInfoStore();// 用户数据模型
let userData = ref({id: '',username: '',nickname: '',
})// 获取登录用户信息
const getUser = async () => {//console.log('getUser userInfo:', userInfoStore.userInfo)userData.value.username = userInfoStore.userInfo!.usernameuserData.value.id = userInfoStore.userInfo!.id
}
getUser()// 折叠按钮处理
const emits = defineEmits(['parentClick']);
const isMenuOpen = ref(false);
const toggleCollapse = () => {isMenuOpen.value = !isMenuOpen.value;//console.log(isMenuOpen.value);emits('parentClick', isMenuOpen.value);
}// 全屏----------------
const fullScreenHander = () => {let full = document.fullscreenElement//console.log(full)if(!full) {// document自带的全屏方法document.documentElement.requestFullscreen()}else {// document自带的推出全屏方法document.exitFullscreen()}
}// 退出登录
const exitHander = async () => {await loginOutService()//console.log("exit ==================")// 清空 token 用户 菜单 路由tokenStore.removeToke()userInfoStore.removeInfo()menuStore.removeMenuRouter()// 跳转登录页面router.push("/login")
}// 个人信息
const selfInfoHander = () => {let data = { title: '个人中心', path: '/userInfo', isClose: false, menuName: '个人中心', parentId: 0 }menuStore.tabList.push(data)menuStore.activeTab = "/userInfo"router.push("/userInfo")}</script><style lang="scss" scope>
.header-container {display: flex;flex-direction: row;justify-content: space-between;align-items: center;width: 100%;height: 5vh;border-bottom: 2px solid;border-color: #f5f5f5;
}.left {display: flex;align-items: center; /* 垂直居中子项 */justify-content: center; /* 水平居中子项(如果需要)*/height: 100%;
}.left > div {display: flex;flex-direction: row;justify-content: center;align-items: center;height: 100%;margin: 5px;
}.right {display: flex;justify-content: center; /* 水平居中子项(如果需要)*/align-items: center; /* 垂直居中子项 */
}
.right > div {display: flex;flex-direction: row;justify-content: center;align-items: center;height: 100%;margin: 3px;margin-left: 5px;
}
.right > div:last-child {margin-right: 10px;
}
</style>

2.4 src\views\layout\MainView.vue

使用自定义tabs

 <template><el-container style="height: 99%"><el-header style="height: 30px; padding: 0;"><div class="tabs-container"><tabs :tagsList="tabList" :activePath="activeTab" @childEvent="handChildEvent"></tabs></div><ul v-show="contextMenuVisible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu"><li @click="closeAllTabs">关闭所有</li><li @click="closeOtherTabs">关闭其他</li></ul></el-header><el-main><el-scrollbar><RouterView/></el-scrollbar></el-main></el-container></template>
<script setup lang="ts">
import tabs from '@/components/tabs/index.vue'// 从stors中获取tab数据
import { useMenuStore } from '@/stores/menu'
import { storeToRefs } from 'pinia'
import { useRouter, useRoute  } from 'vue-router'
import { nextTick, onMounted, ref, watch } from 'vue';
const router = useRouter()
const menuStore = useMenuStore();
let { tabList, activeTab } = storeToRefs(menuStore)const route = useRoute()
watch(() => route.path, newRoute=> {console.log("watch route: ", newRoute)menuStore.routeCheage(newRoute)
})// 选中
function gotoTab(path: string) {// 修改activeTabmenuStore.setActiveTab(path);// 路由页面router.push(path);console.log("current tablist======", tabList.value.filter(item => item.path == path))let currentTab = tabList.value.filter(item => item.path == path)// 添加面包屑menuStore.addBreadList(currentTab[0])}
// 移除
function closeTab(path: string) {// 移除的是否是当前激活的tab,激活首页console.log("closetab path=", path)if (path == menuStore.activeTab) {menuStore.setActiveTab("index")router.push("index")}menuStore.delTabList(path)
}// 关闭contextMenu
const closeContextMenu = () => {contextMenuVisible.value = false
}// 关闭所有标签页
const closeAllTabs = () => {contextMenuVisible.value = false;menuStore.initBreadcrumbAndTabs()router.push("/index")
}
// 关闭其它标签页
const closeOtherTabs = () => {menuStore.colseOthersTabs(currrentPath.value)contextMenuVisible.value = false
}// 子组件传值
const handChildEvent = (data: any) => {console.log("handChildEvent,", data)if (data.type == 'close') {menuStore.delTabList(data.path)} if (data.type == 'active') {menuStore.setActiveTab(data.path)router.push(data.path)}if (data.type == 'rightClick') {currrentPath.value = data.pathcontextMenuVisible.value = trueleft.value = data.lefttop.value = data.top}
}// 右键标签
const contextMenuVisible = ref(false)
const left = ref()
const top = ref()
const currrentPath = ref()watch(contextMenuVisible, (newValue, oldValue) => {if (newValue) {document.body.addEventListener("click", closeContextMenu)} else {document.body.removeEventListener("click", closeContextMenu)}
})</script><style lang="scss" >.tabs-container {height: 30px;margin: 5px;}.el-tabs {--el-tabs-header-height: 30px;
}.demo-tabs > .el-tabs__content {padding: 0;
}
.demo-tabs > .el-tabs__header {margin: 0 0 5px 0;
}/* 弹出框 */
.contextmenu {width: 100px;margin: 0;border: 1px solid #ccc;background: #fff;z-index: 3000;position: absolute;list-style-type: none;padding: 5px 0;border-radius: 4px;font-size: 14px;color: #333;box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.2);
}.contextmenu li {margin: 0;padding: 7px 16px;
}.contextmenu li:hover {background: #f2f2f2;cursor: pointer;
}
</style>

2.5 src\views\layout\Footer.vue

<template><div class="footer-container"><span class="footer-span">Vue3 TS Vite Springboot3 快速开发平台 ©2024 Created by cc</span></div>
</template><style lang="scss">
.footer-container {display: flex;flex-direction: row;justify-content: center;align-items: center;height: 90%;border-top: 2px solid;border-color: #f5f5f5;.footer-span {font-size: 11px;}
}
</style>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 使用 prerenderRoutes 进行预渲染路由
  • 设计模式实战:旅行预订系统的设计与实现
  • Java 操作 Redis和redis持久化
  • C++图笔记(二)最短路
  • 【速通C语言(纯小白版)】第一部分:准备工作
  • microsoft edge怎么关闭安全搜索
  • 【ubutnu18.04】k8s 部署4: worker节点配置1.31.0和containerd 1.7.20
  • Golang | Leetcode Golang题解之第338题比特位计数
  • 【学习笔记】卫星网络(NTN)的窄带物联网(NB-IoT)/增强型机器类型通信(eMTC)研究 -- 3GPP TR 36.763(四)
  • 如何将CSDN文章导出为pdf文件
  • 【数据结构-哈希前缀】力扣2845. 统计趣味子数组的数目
  • 多线程锁机制面试
  • 素数与最大公约数GCD:
  • 基于MVC模式的红色革命文物征集管理系统的设计与实现--论文pf
  • 技术速递|Python in Visual Studio Code 2024年8月发布
  • JAVA SE 6 GC调优笔记
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Vue 重置组件到初始状态
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 简单易用的leetcode开发测试工具(npm)
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 思维导图—你不知道的JavaScript中卷
  • 无服务器化是企业 IT 架构的未来吗?
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 赢得Docker挑战最佳实践
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 数据可视化之下发图实践
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​​​​​​​​​​​​​​Γ函数
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​字​节​一​面​
  • # .NET Framework中使用命名管道进行进程间通信
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #include到底该写在哪
  • #LLM入门|Prompt#3.3_存储_Memory
  • #pragam once 和 #ifndef 预编译头
  • (1)无线电失控保护(二)
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (八)Flink Join 连接
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (五)MySQL的备份及恢复
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • .md即markdown文件的基本常用编写语法
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • // an array of int
  • @31省区市高考时间表来了,祝考试成功
  • @Conditional注解详解
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚
  • @NotNull、@NotEmpty 和 @NotBlank 区别