在webpack5 +Vue3 的基础上,配合element ui 搭建了基础后台页面 ,包含layout +导航栏+路由
登录/注册页面切换方式
index.vue
<template><el-container><!-- 侧边导航栏 --><el-aside:class="{open: collapseStatus == false && currentWidth > 800,close: collapseStatus == true && currentWidth > 800,hidden: currentWidth <= 800}"><SideBar :collapseStatus="collapseStatus" :CurrentWidth="currentWidth"></SideBar></el-aside><el-container><!-- 顶层导航栏 --><NavBar @collapseStatus="setcollapse" :CurrentWidth="currentWidth"></NavBar><!-- 主要内容 --><el-main><AppMain></AppMain></el-main><el-footer>叮咚买菜!您菜到家了!</el-footer></el-container></el-container></template><script>import { NavBar, SideBar, AppMain } from './components'export default {data() {return {collapseStatus: false,currentWidth: 0}},components: {NavBar,SideBar,AppMain},mounted() {this.getWidth()},methods: {//侧边栏打开关闭状态ssetcollapse(params) {// console.log('关闭打开状态', params)this.collapseStatus = params},getWidth() {this.currentWidth = document.body.clientWidth// console.log(this.currentWidth)window.onresize = () => {this.currentWidth = document.body.clientWidthconsole.log(this.currentWidth)}}}}</script><style lang="scss" scoped>.el-container {height: 100%;width: 100%;border: 0px;// .el-aside {// display: flex;// flex-direction: column;// text-align: left;// }//屏幕宽度>800.open {width: 200px;}.close {width: 75px;::v-deep .menuItem > span {display: none;}::v-deep .el-submenu__icon-arrow.el-icon-arrow-down {display: none;}}//屏幕宽度<800.hidden {width: 0px;}}.el-footer {text-align: center;line-height: 60px;padding: 0px;overflow: hidden;}.el-main {background-color: white;margin: 10px 10px 0px;box-shadow: -1px 1px 5px #888888;padding: 16px;color: #333;}</style><style lang="scss">// 修改导航竖排模式下的样式.el-popper.is-light.is-pure {border: none;box-shadow: -2px 2px 2px #333;}</style>
components
index.js
export { default as NavBar } from './Navbar.vue'export { default as SideBar } from './SideBar.vue'export { default as AppMain } from './AppMain.vue'
AppMain.vue
主内容
<template><section class="app-main"><router-view :key="key" /></section></template><script>export default {name: 'AppMain',computed: {key() {return this.$route.path}}}</script><style scoped>.app-main {/*60 = navbar *//* min-height: calc(100vh - 60px); */min-height: 100%;width: 100%;position: relative;overflow: hidden;}/* .fixed-header + .app-main {padding-top: 50px;} */</style><style lang="scss">// fix css style bug in open el-dialog// .el-popup-parent--hidden {// .fixed-header {// padding-right: 15px;// }// }</style>
NavBar.vue
横排导航栏
<template><el-header><el-menu class="NavBar" mode="vertical" text-color="#fff" active-text-color="#ffd04b"><!-- 打开收起 --><!-- width>800 PC 展示 --><div @click="iscollapse" class="collapse_content" v-if="currentWidth > 800"><i class="el-icon-s-fold" v-if="collapse == false"></i><i class="el-icon-s-unfold" v-if="collapse"></i></div><!-- width<800 抽屉 展示 --><div v-if="currentWidth <= 800" class="collapse_content" @click="isdrawer"><i class="el-icon-menu"></i></div><!-- 抽屉展示 --><el-drawer :with-header="false" v-model="drawer" direction="ltr" destroy-on-close size="150px"><SideBar></SideBar></el-drawer><!-- 面包屑 --><div :class="{ breadcrumb_container: currentWidth > 400, breadcrumb_hidden: currentWidth <= 400 }"><NavBread></NavBread></div><el-dropdown trigger="hover"><span class="el-dropdown-link"><el-tooltip class="item" effect="dark" content="admin菜到了" placement="left"><span><img src="@/assets/avatar.png" /></span></el-tooltip><i class="el-icon-arrow-down"></i></span><template #dropdown><el-dropdown-menu><el-dropdown-item icon="el-icon-s-home" @click="backHome"><span>首页</span></el-dropdown-item><el-dropdown-item icon="el-icon-switch-button" @click="logOut"><span>登出</span></el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-menu></el-header></template><script>import NavBread from './NavBread.vue'import SideBar from './SideBar.vue'import { getToken, removeToken } from '@/utils/auth'import { ElMessage } from 'element-plus'export default {data() {return {collapse: false, //false打开,true关闭drawer: false,currentWidth: 0,isChoosed: false}},props: {CurrentWidth: {type: Number}},components: {NavBread,SideBar},watch: {CurrentWidth() {this.currentWidth = this.CurrentWidththis.drawer = false //窗口变化直接关闭抽屉if (this.CurrentWidth > 800) {this.drawer = false //窗口样式不属于抽屉时关闭抽屉}// if (this.CurrentWidth > 800 && this.CurrentWidth <= 1000) {// this.collapse = true// this.$emit('collapseStatus', this.collapse)// } else if (this.CurrentWidth > 1000) {// this.collapse = false// this.$emit('collapseStatus', this.collapse)//震惊 !!!!这是个bug, 在1000以上启动项目时,能正常缩放///// 但小于1000下启动项目会造成路由出问题//未解决,暂时去除}},mounted() {},methods: {isLogin() {var token = getToken('user_token')if (token == undefined || token == '' || token == null) {this.$router.push('/login')}},logOut() {removeToken()this.isLogin()ElMessage.success('成功退出')},backHome() {//返回首页this.$router.push('/home')},iscollapse() {//打开关闭侧边栏this.collapse = !this.collapsethis.$emit('collapseStatus', this.collapse)},//抽屉导航打开状态isdrawer() {this.drawer = !this.drawer// console.log(11111, this.drawer)}// hasChoose(params) {// this.isChoosed = params// console.log('选择', this.isChoosed)// if (this.isChoosed) {// this.drawer = false// this.isChoosed = false// }// }}}</script><style lang="scss" scoped>//NavBar样式.el-header {text-align: center;line-height: 60px;padding: 0px;box-shadow: 3px 3px 5px #888888;background-color: white;.NavBar {border: 0px;}}//打开关闭侧边栏按钮样式.collapse_content {float: left;cursor: pointer;height: 100%;font-size: 25px;padding: 0 15px;}//面包屑样式.breadcrumb_container {float: left;.el-breadcrumb {font-size: 13px;line-height: 60px;}}.breadcrumb_hidden {display: none;}//抽屉弹窗::v-deep .el-drawer.ltr {width: 150px;}//头像样式::v-deep .el-dropdown {float: right;.el-dropdown-link {display: flex;align-items: center;padding-right: 20px;font-size: 18px;img {height: 40px;width: 40px;padding: 10px;border-radius: 25px;}}}::v-deep .el-popper.is-light.is-pure {border: none;}::v-deep .el-menu.el-menu--horizontal{border: none;}// ul.el-menu.el-menu--popup.el-menu--popup-bottom-start</style>
NavBreade.vue
面包屑
<template><el-breadcrumb separator="/"><el-breadcrumb-item :to="{ path: '/home' }">Ebuy</el-breadcrumb-item><el-breadcrumb-item v-for="item in pathMap" :key="item.meta">{{ item.meta.title }}</el-breadcrumb-item></el-breadcrumb></template><script>export default {data() {return {pathMap: ''}},mounted() {this.getPath()},methods: {getPath() {const route = this.$routeconst { matched } = routethis.pathMap = matched//过滤掉不存在meta.titie的路由this.pathMap = this.pathMap.filter(function (item) {return item.meta.title != null})}},//监听路由变化watch: {$route: 'getPath'}}</script><style scoped></style>
SiderBar
左侧导航栏
<template><div v-if="CurrentWidth > 800" class="aside"><img src="@/assets/ebuy-logo.png" class="SideBar_logo" /><el-menuref="asideMenu":default-active="activeMenu":uniqueOpened="true":mode="mode":menu-trigger="menuOpenMethods"class="SideBar_content"background-color="#304156"text-color="#bfcbd9"active-text-color="#409EFF"router><AsideBarItem v-for="route in routes" :key="route.path" :item="route"></AsideBarItem></el-menu></div><!-- 小于800的样式要重写 --><div v-else class="aside_mini"><!-- <img src="@/assets/ebuy-logo.png" class="SideBar_logo" v-if="CurrentWidth > 800" /> --><el-menu:default-active="activeMenu":uniqueOpened="true"mode="horizontal":menu-trigger="menuOpenMethods"class="SideBar_content"background-color="#304156"text-color="#bfcbd9"active-text-color="#409EFF"router><AsideBarItem v-for="route in routes" :key="route.path" :item="route"></AsideBarItem></el-menu></div></template><script>import AsideBarItem from './asideItem.vue'export default {components: {AsideBarItem},props: {collapseStatus: {type: Boolean},CurrentWidth: {type: Number}},watch: {collapseStatus() {if (this.collapseStatus == false) {this.mode = 'vertical'} else {this.mode = 'horizontal'this.closeMenu() //当路由模式变化时自动关闭当前激活的路由}}// CurrentWidth() {// if (this.CurrentWidth <= 1000) {// this.mode = 'horizontal'// } else if (this.CurrentWidth > 1000) {// this.mode = 'vertical'// }// }},data() {return {mode: 'vertical',menuOpenMethods: 'hover',drawer: false,closePath: ''}},computed: {routes() {//返回路由return this.$router.options.routes},activeMenu() {//保持左侧导航栏的激活状态const route = this.$routeconst { path } = routethis.closePath = path.split('/')[1] //分割路由 用于关闭当前激活的路由导航return path}},methods: {closeMenu() {this.$refs['asideMenu'].close('/' + this.closePath) //关闭打开的menu}// handleSelect(hasChoose) {// this.$emit('hasChoose', true)// }}}</script><style lang="scss" scoped>.el-menu {border: none;}.aside {display: flex;flex-direction: column;height: 100%;text-align: left;.SideBar_logo {height: 60px;width: 100%;}.SideBar_content {flex: 1;}}.aside_mini {display: flex;text-align: left;flex-direction: column;height: 100%;.SideBar_content {flex: 1;}}::v-deep .el-submenu .el-menu-item {height: 50px;line-height: 50px;padding: 0 45px;min-width: 100px;}</style><style lang="scss">//保持激活状态的颜色.el-submenu.is-active > .el-submenu__title {.menuItem > i {color: #409eef !important ;}color: #409eef !important ;}</style>
asideItem.Vue
左侧导航栏每一栏展示 ,与router 配合使用
<template><!-- 路由不隐藏就展示在侧边栏上 --><div class="AsideBarItem" v-if="!item.hidden"><el-submenu :index="item.path" v-if="item.children && item.children.length > 1"><!-- 如果存在子路由,并且超过一个 --><template #title><div class="menuItem"><i :class="item.meta.icon"></i><span>{{ item.meta.title }}</span></div></template><!-- 递归 --><aside-item v-for="child in item.children" :key="child.path" :item="child"></aside-item></el-submenu><!-- 存在子路由,但只有一个就只展示子路由 --><el-menu-item :key="item.children[0].path" :index="item.children[0].path" v-else-if="item.children && item.children.length == 1"><div class="menuItem"><i :class="item.children[0].meta.icon"></i><span>{{ item.children[0].meta.title }}</span></div></el-menu-item><!-- 不存在子路由展示本身 --><el-menu-item :key="item.path" :index="item.path" v-else-if="item.meta.isShow === true"><div class="menuItem"><i :class="item.meta.icon"></i><span>{{ item.meta.title }}</span></div></el-menu-item></div></template><script>export default {props: {item: {type: Object,required: true}},data() {return {}},method: {}}</script><style lang="scss" scoped>.menuItem > i {margin-right: 0px;color: #fff;}::v-deep .el-submenu__title {.el-submenu__icon-arrow {font-size: 18px;}}.menuItem {span {font-size: 16px;}}</style>
router.js
import { createRouter, createWebHashHistory } from 'vue-router'import { getToken } from '@/utils/auth'import { ElMessage } from 'element-plus'import Home from '@/views/Home.vue'import Login from '@/views/Login/Login-swipe.vue'import notFound from '@/views/404.vue'import layout from '@/Layout/index.vue'import AppMain from '@/Layout/components/AppMain.vue'import addShop from '@/views/shop/addShop.vue'import shopList1 from '@/views/shop/shopList.vue'import shopList2 from '@/views/shop/shopListTest.vue'const routerHistory = createWebHashHistory()const router = createRouter({history: routerHistory,routes: [{path: '/login',component: Login,hidden: true},// 首页{path: '/',component: layout,children: [{path: '/home',component: Home,meta: { title: '首页', icon: 'el-icon-s-home', isShow: true }}]},// 商品{path: '/shop',component: layout,meta: {title: '商品',icon: 'el-icon-pie-chart'},children: [{path: '/shop/shopList',component: AppMain,meta: { title: '商品列表', icon: 'el-icon-menu', isShow: true },children: [{path: '/shop/shopList/test1',component: shopList1,meta: { title: '商品列表测试', icon: 'el-icon-s-marketing', isShow: true }},{path: '/shop/shopList/test2',component: shopList2,meta: { title: '商品列表测试2', icon: 'el-icon-s-custom', isShow: true }}]},{path: '/shop/addshop',component: addShop,meta: { title: '添加商品', icon: 'el-icon-s-claim', isShow: true }}]},//notFound{path: '/',component: layout,hidden: true, //将404放进route中,但不展示children: [{path: '/404',component: notFound,meta: { isShow: false }}]},{ path: '/:pathMatch(.*)', redirect: '/404', hidden: true }]})router.beforeEach((to, from, next) => {// 1.如果访问的是登录页面(无需权限),直接放行if (to.path === '/login') return next()// 2.如果访问的是有登录权限的页面,先要获取tokenconst tokenStr = getToken('user_token')// 2.1如果token为空,强制跳转到登录页面;否则,直接放行if (!tokenStr || tokenStr == 'null' || tokenStr == undefined) {ElMessage({message: '用户信息已失效!请重新登录',type: 'error'})return next('/login')}next()})export default router
