【Vue3+Element Plus】从0-1搭建后台管理系统(03)-Header-左侧

838 阅读2分钟

【Vue3+Element Plus】从0-1搭建后台管理系统(03)-Header-左侧侧

这一章记录完成Header顶部左侧区域的静态布局实现过程。

顶部区域的部分包含了侧边栏折叠图标、全屏、面包屑、消息通知图标、用户头像、主题切换开关(白昼) 的渲染。

创建对应目录以及组件

  1. layout目录下新建Header文件夹
  2. Header文件夹下新建index.vue
  3. Header目录下新建components文件夹,用来存放用到的组件
  4. Header目录下新建Nav.vue顶部导航栏组件、Tags.vue标签栏组件以及Breadcrumb.vue面包屑组件。其余的组件可以根据自己需要自行创建,例如:用户头像之类的。

image.png

顶部导航栏(Nav)

顶部导航栏我将其分为左侧和右侧两个区域,左侧用来展示侧边栏展开收起图标以及面包屑,右侧用来展示用户头像等。使用flex布局实现左右两侧布局,具体代码如下:

<style lang="scss" scoped>
.nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px;
  height: 50px;
  border-bottom: 1px solid $border-color;
}
</style>

左侧

图标

  • 图标这块我选择的是iconify

  • 安装:

    npm install --save-dev @iconify/vue
    
  • 封装:

    src/components目录下创建Icon专门用来放图标组件,然后在此目录下创建IconifyIcon.vue

    <template>
        <IconifyIcon :icon="iconName" :style="{ ...attr }" />
    </template>
    
    <script setup lang="ts">
      import { Icon as IconifyIcon } from '@iconify/vue'
      import { useAttrs } from 'vue'
      
      defineOptions({
        name: 'IconifyIcon'
      })
    
      /** 父组件绑定在子组件上但子组件没有使用props接收的属性 */
      const attr = useAttrs()
    
      /** 用来接受父组件传的值 */
      const props = withDefaults(defineProps<{ iconName: string }>(), {
        iconName: ''
      })
    </script>
    
    <style lang="scss" scoped></style>
    
    

    这里需要注意一下,图标的容器高度会多出几个像素,上网查找了一下解决方法,给svg图标的容器添加display: flex即可解决。

    Nav.vue中引入使用:

    <template>
        <div class="nav">
          <!-- 左侧区域 -->
          <div class="nav-left">
            <!-- 折叠图标 -->
            <iconify-icon icon-name="ant-design:menu-fold-outlined"></iconify-icon>
            <!-- 面包屑 -->
          </div>
          <!-- 右侧区域 -->
          <div class="nav-right"></div>
        </div>
    </template>
    <script setup lang="ts">
    /** 引入iconify-icon */
    import IconifyIcon from '@/components/Icon/IconifyIcon.vue'
    
    defineOptions({
      name: 'DTNav'
    })
    </script>
    

image.png

控制侧边栏的展开/收起

image.png

控制侧边栏的展开和收起需要一个开关去进行控制,这个开关不单单是控制侧边栏的展开和收起,并且控制着顶部左侧图标的一个切换。

因为这个开关值在两个没有任何关联的组件中都使用到了,所以我将这个开关定义在pinia中:

  • 创建一个pinia仓库:

    image.png

    import { defineStore, type Store } from 'pinia'
    import { ref } from 'vue'
    import { store } from '../index'
    
    export const useLayoutStore = defineStore('layout', () => {
      /** 控制侧边栏是否收起 */
      const isCollapse = ref(false)
    
      /** 切换isCollapse的状态 */
      const changeIsCpllapse = (): void => {
        isCollapse.value = !isCollapse.value
      }
    
      return { isCollapse, changeIsCpllapse }
    })
    
    /**
     * 用于setup语法糖以外的地方使用
     * @returns {Store}
     */
    export const useLayoutStoreHook = (): Store => {
      return useLayoutStore(store)
    }
    
  • Nav.vue组件中控制图标的切换:

    <template>
     <!-- 折叠图标 -->
        <iconify-icon
            :icon-name="isCollapse ? 'ant-design:menu-unfold-outlined' : 'ant-design:menu-fold-outlined'"
            @click="changeIsCpllapse">
        </iconify-icon>
    </template>
    <script setup lang="ts">
    /** 引入 layout store */
    import { useLayoutStore } from '@/store/modules/layout'
    /** 引入storeToRef()函数,从store中获取属性保持响应性 */
    import { storeToRefs } from 'pinia'
    defineOptions({
      name: 'DTNav'
    })
    
    /** layoutstore */
    const layoutStore = useLayoutStore()
    
    /** 获取layoutstore中的方法 */
    const { changeIsCpllapse } = layoutStore
    
    /** 获取layoutstore中的属性 */
    const { isCollapse } = storeToRefs(layoutStore)
    
    console.log('isCollapse', isCollapse)
    </script>
    
  • Sidebar/index.vue组件中引入并控制el-menu的收起/展开:

    <template>
        <el-menu
          default-active="2"
          background-color="#001428"
          text-color="#BBB"
          active-text-color="#fff"
          :collapse="isCollapse"
        >
    </template>
    
    <script setup lang="ts">
    /** 引入 layoutstore */
    import { useLayoutStore } from '@/store/modules/layout'
    /** 引入storeToRef()函数,从store中解构属性保持响应性 */
    import { storeToRefs } from 'pinia'
    defineOptions({
      name: 'SideBar'
    })
    
    /** 创建useLayoutStore */
    const layoutStore = useLayoutStore()
    
    /** 获取layoutStore中的属性 */
    const { isCollapse } = storeToRefs(layoutStore)
    </script>
    
  • layout/index.vue组件中引入并控制el-aside的收起/展开:

    <template>
      <el-aside :width="isCollapse ? '65px' : '200px'">
        <SideBar />
      </el-aside>
    </template>
    
    <script setup lang="ts">
    import SideBar from './SideBar/index.vue'
    /** 引入useLayoutStore */
    import { useLayoutStore } from '@/store/modules/layout'
    /** storeToRefs(),从store中解构属性保持响应性 */
    import { storeToRefs } from 'pinia'
    defineOptions({
      name: 'Layout'
    })
    
    /** 创建layoutStore */
    const layoutStore = useLayoutStore()
    
    /** 从layoutStore中解构出isCollapse属性 */
    const { isCollapse } = storeToRefs(layoutStore)
    </script>
    
    <style>
      .el-aside {
        ...
        transition: width 0.5s;
      }
    </style>
    
  • 侧边栏logo的变化

    logo的变化指的是在侧边栏收起的时候,logo只展示图片不展示文字:

    <template>
       <div class="logo">
         <img class="logo-image" :src="getImageUrl('logo.png')" alt="logo.png" />
         <Transition enter-active-class="animate__animated animate__fadeInDown" mode="out-in">
           <h1 class="logo-text" v-if="!isCollapse">DaiTuAdmin</h1>
         </Transition>
       </div>
    </template>
    <script setup lang="ts">
     /** 引入useLayoutStore */
     import { useLayoutStore } from '@/store/modules/layout'
     /** 引入storeToRefs(),从store中解构属性保持响应性 */
     import { storeToRefs } from 'pinia'
     /** 引入animate.css */
     import 'animate.css'
     defineOptions({
       name: 'Logo'
     })
    
     /** 创建layoutStore */
     const layoutStore = useLayoutStore()
    
     /** 从layoutStore中解构属性 */
     const { isCollapse } = storeToRefs(layoutStore)
    </script>
    

最终的效果:

collapse.gif

面包屑的渲染

面包屑也是同样在左侧展示,用到Element-plus的Breadcrumb组件:

首先还是将我们需要使用到的element-plus的组件引入进行全局注册。

然后在Breadcrumb.vue组件中进行使用:

Breadcrumb.vue:

<template>
  <div class="breadcrumb">
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">homepage</el-breadcrumb-item>
      <el-breadcrumb-item>promotion management</el-breadcrumb-item>
      <el-breadcrumb-item>promotion list</el-breadcrumb-item>
      <el-breadcrumb-item>promotion detail</el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

<script setup lang="ts">
defineOptions({
  name: 'Breadcrumb'
})
</script>

<style lang="scss" scoped></style>

然后在Nav.vue组件中引入并使用:

<!-- 左侧区域 -->
<template>
    <div class="nav-left">
      <!-- 折叠图标 -->
      <i class="nav-left__iscollapse">
        <component :is="renderIconIfyIcon('ant-design:menu-fold-outlined')" />
      </i>
      <!-- 面包屑 -->
      <Breadcrumb />
    </div>
</template>

<script setup lang="ts">
/** 引入面包屑组件 */
import Breadcrumb from './Breadcrumb.vue'
</script>

image.png