从0开始搭建后台管理系统-02(Layout布局)(下篇)

1. 介绍

书接上一回,我们说完了header部分的内容,如果没有看过的可以在其他文章中跳转。本篇介绍sideBar中的内容。
功能:sideBar主要是展示菜单,其中包括:多级菜单、外链、单个子路由合并
注:涉及到路由方面都会涉及到动态路由,也就是路由权限管理,继续挖坑嘿嘿,等待下篇讲解。

2. 详解

2.1 介绍

  1. sideBar结构很简单,上面是后台管理的logo或名称,下面是一个菜单栏。
  2. 菜单栏中如果不止一个子路由就可以展开展示,如果只有一个子路由且是最终的子路由(子路由没有子路由)我进行了合并成为父路由,单个路由展开有些奇怪。
    多级嵌套:我们是不限制嵌套多少级子路由的,所以这里得使用一个递归组件的思想。
    vue3文档:一个单文件组件可以通过它的文件名被其自己所引用。例如:名为 FooBar.vue 的组件可以在其模板中用 <FooBar/> 引用它自己。

2.2 实现

最外层使用的是el-munu,定义了一个子组件aside-item用来进行自我递归,传入的是当前角色的动态路由+静态路由。样式取的是定义的主题中的变量。

// layout > components > sideBar > index.vue
<template>
  <div class="aside-wrapper">
    <section class="logo-wrapper" >
      <p style="color:#fff; font-size: 50px; text-align: center">hr</p>
    </section>
    <el-menu
        default-active="dashboard"
        :collapse="isCollapse"
        :unique-opened="true"
        :collapse-transition="false"
    >
      <aside-item
          :menu="menu"
          :icon-show="true"
          :is-collapse="isCollapse"
      />
    </el-menu>
  </div>
</template>

<script lang="ts" setup>
import {computed} from 'vue'
import AsideItem from './componts/asideItem.vue'
import {useUserStore} from '@/store/user'

const props = defineProps({
  isCollapse: {
    type: Boolean,
    default: false
  }
})

// 当前角色全部路由
const userStore = useUserStore()
const menu = userStore.routes

// 获取主题中定义的aside宽度
const asideWidth = computed(()=>props.isCollapse? 'var(--aside-min-width)': 'var(--aside-max-width)')

</script>
<!---->
<style lang="less" scoped>
.aside-wrapper {

  width: v-bind(asideWidth);
  height: calc(100% - @tag-height);
  transition:.5s;
  background-color: @theme-aside-bg-color;

  .logo-wrapper {
    height: calc(@header-height + @tag-height);
    line-height: calc(@header-height + @tag-height);
  }

  :deep(.el-menu-item){
    &.is-active {
      background-color: @theme-aside-active-bg-color;
    }
  }
}
</style>
  1. 通过计算属性,去计算是否是多级或者是单级。多级用el-sub-menu,在将内容进行递归,单级使用el-menu-item。
  2. 每个item的key为路由的path,如果不是外链就直接路由跳转,如果是外链单独跳转。
  3. 只有一级路由会有icon,每级路由都有名称。
    主要实现如下:

// sideBar > components > asideItem

 <template>
  <template v-for="route in menu">
    <el-sub-menu v-if="!isOneItemShow(route)"
                 :class="{'aside-collapse':isCollapse}"
                 :index="route.path">

      <template #title>
        <svg-icon class="aside-svg"
                  v-if="iconShow"
                  :name="route?.meta?.svgIcon"
        />
        <span class="aside-title">
          {{ route?.meta?.title }}
        </span>
      </template>

      <aside-item
          :menu="route.children"
      />
    </el-sub-menu>
    <el-menu-item v-else
                  :class="{'aside-collapse':isCollapse}"
                  @click="clickMenuItem(route.children && route.children[0] || route)"
                  :index="route.path"
    >
      <svg-icon v-if="iconShow"
                class="aside-svg"
                :name="route?.meta?.svgIcon"
      />
      <template #title>
        <span class="aside-title">
        {{ route?.meta?.title }}
        </span>
      </template>
    </el-menu-item>

  </template>
</template>

<script setup lang="ts">

import {useRouter} from "vue-router";

// TODO 组件遍历自身
const props = defineProps({
  menu: {
    type: Object,
    require: true,
  },
  iconShow: {
    type: Boolean,
    default: false
  },
  isCollapse: {
    type: Boolean,
    require: true
  }
})

const router = useRouter()

// 过滤隐藏
const menu = props.menu?.filter(route => !route.meta.sidebarHide)


// 判断单个路由 和 是否合并显示
const isOneItemShow = (router) => {
  if(router.children){
    if ( router.children.length > 1 || router.children[0].children) {
      return false
    }
  }
  return true
}

// 点击后跳转至对应路由
const clickMenuItem = (item) => {
  if (item?.meta?.isExt) {
    window.open(item.path, '_blank')
  } else {
    router.push(item.path)
  }
}
</script>

<style lang="less" scoped>
.aside-svg {
  box-sizing: content-box;
  margin: 0 0.5em;
  font-size: 1.2em;
}
.aside-collapse {
  :deep(.el-tooltip__trigger) {
    padding: 0;
  }
  .aside-svg {
    width: 100%;
  }
}
</style>

2.3 main

  1. 使用router-view的插槽方式去拓展动画和keep
    alive等额外功能
  2. 我们在constants中定义了需要保活的列表,keep-alive 中支持include传入路由列表name值,其他方式没尝试成功,道友可以试一试。
// layout > index.vue
<template>
  <section class="layout-wrapper">
    <aside class="left-wrapper">
      <my-side :is-collapse="isCollapsed"/>
    </aside>
    <div class="right-wrapper">
      <header>
        <my-header v-model:isCollapsed="isCollapsed">
        </my-header>
      </header>
      <main class="scrollbar">
        <router-view v-slot="{ Component, route }">
          <transition name="fade">
              <keep-alive :include="KEEP_ALIVE_COMPONENT">
                <component :is="Component" :key="route.path"/>
              </keep-alive>
          </transition>
        </router-view>
      </main>
    </div>
  </section>
</template>


<script setup lang="ts">
import {onMounted, watch, ref, computed} from 'vue'
import MyHeader from './components/header/index.vue'
import MySide from './components/sideBar/index.vue'
import cache from '@/utils/cache'
import {KEEP_ALIVE_COMPONENT} from '@/constants/setting'
import {IS_COLLAPSED_KEY} from '@/constants/cacheKeys'

const isCollapsed = ref<boolean>(false)

onMounted(()=>{
  isCollapsed.value =  cache.getItem(IS_COLLAPSED_KEY)
})
watch(()=>isCollapsed.value,()=>{
  cache.setItem(IS_COLLAPSED_KEY,isCollapsed.value)
})

</script>

<style lang="less" scoped>
body {
  background-color: @theme-bg-color;
}

.theme-style{
  color: @theme-font-color;
  background-color: @theme-bg-color;
}

.layout-wrapper {
  display: flex;

  .left-wrapper {
    z-index:999;
    position: sticky;
    top: 0;
    left: 0;
    flex-shrink: 0;
    overflow-x: hidden;
    overflow-y: auto;
    height: 100vh;
  }

  .right-wrapper {
    flex:1;
    .theme-style();
    header{
      .theme-style();
      background-color: @theme-color;
    }
    main{
      overflow-y: scroll;
      position: relative;
      height: calc( 100% - 10px * 2 - @header-height - @tag-height );
      margin: 10px;
      background-color: @theme-color;
    }
  }
}
</style>

整体框架就是这样了,写的有些粗糙,有问题可以私聊,有什么建议可以提出来,大家想办法一起改进。

3. 本框架其他文章链接

GitHub开源链接:GitHub - grxynl/vue3-admin-template: vue3+TypeScript+pinia 后台管理系统模板

其他文章
从0开始搭建后台管理系统(首篇)
从0开始搭建后台管理系统-01(Login登录)
从0开始搭建后台管理系统-02(Layout布局)(上篇)

4. 结束语

本框架完全免费,框架尚有不足,供前端爱好者一起讨论,一起学习。
源码和文档都制作不易,如果觉得您还可以的话,求一个stars,这是对我最大的支持,也是本框架前进的最大动力。

5. 引用及感谢

vue3官方文档

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值