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

devtools/2024/10/19 2:20:27/

文章目录

  • 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>

http://www.ppmy.cn/devtools/97048.html

相关文章

扫雷基础与进阶(附有详细解析)

前言&#xff1a;对于基础版扫雷&#xff0c;你需要掌握的知识有&#xff1a;循环与分支、函数基础、二维数组以及随机数函数&#xff08;不懂可以看看我这篇文章《随机数函数 和 猜数字游戏》&#xff0c;需要了解rand,srand,time这三个函数&#xff09;&#xff1b;对于进阶版…

VueX 使用

1.简介 就是用来多组件共享数据的实现用的 2.使用VueX 因为使用的是vue2 所以下的是vuex3 若是vue3 必须下的是 vue4 npm i vuex3 3.搭建环境 1.创建 src/store/index.js //该文件用于创建一个Vuex中最为核心的store//引入VueX import Vuex from vuex import Vue from vu…

[ABC367C] Enumerate Sequences 题解

[ABC367C] Enumerate Sequences 搜索。 考虑使用 DFS 深搜&#xff0c;对于第 i i i 个数&#xff0c;从 1 1 1 到 r i r_i ri​ 枚举&#xff0c;将 a i a_i ai​ 设为当前枚举的数&#xff0c;并进行下一层递归。 对所有的数填完后&#xff0c;判断当前和是否为 k k …

MyCAT读写分离及实现---MySQL5.7的glibc

中间件代理方式的读写分离&#xff0c;在业务代码中&#xff0c;数据库的操作不直接连接数据库&#xff0c;而是先请求到中间件服务器&#xff08;代理&#xff09;&#xff0c;由代理服务器判断是读操作去从数据服务器&#xff0c;写操作去主数据服务器。 中间件代理服务器&am…

SpringBoot MySQL BinLog 监听数据变化(多库多表)

开始 1&#xff1a;引入mysql-binlog-connector-java.jar <!-- binlog --><dependency><groupId>com.zendesk</groupId><artifactId>mysql-binlog-connector-java</artifactId><version>0.27.1</version></dependency>…

使用DOM破坏启动xss

目录 实验环境&#xff1a; 分析&#xff1a; 找破坏点&#xff1a; 查看源码找函数&#xff1a; 找到了三个方法&#xff0c;loadComments、escapeHTM 、displayComments loadComments escapeHTM displayComments&#xff1a; GOGOGO 实验环境&#xff1a; Lab: Exp…

前端面试——js作用域

说一说JS的作用域吧 作用域的分类 作用域分为&#xff1a;全局作用域&#xff0c;函数作用域&#xff0c;块级作用域 作用域的特性 全局作用域&#xff1a; 能够让变量和函数在全局位置访问&#xff0c;其挂载在浏览器的window对象下面 其中var定义的变量和function函数存…

品牌出海新策略:携手TikTok达人,合作孵化IP实现双赢

在当今数字化时代&#xff0c;TikTok达人的IP孵化作为一种创新的合作模式&#xff0c;正逐渐成为品牌出海的新兴策略。通过与有潜力的TikTok达人合作&#xff0c;共同孵化新的IP&#xff0c;品牌不仅能够突破传统营销的局限&#xff0c;还能实现与达人共同成长的双赢局面。本文…