【vue3后台项目】实现Sidebar的其他功能

左侧菜单伸缩功能实现

接着我们来实现一个左侧菜单伸缩,这个功能的核心点在于动画的处理;样式的改变总是由数据进行驱动,所以我们首先去创建对应的数据;

创建store/modules/app.js,写入代码:

app.js 复制代码
export default {
  namespaced: true,
  state: () => ({
    sidebarOpened: true
  }),
  mutations: {
    triggerSidebarOpened(state) {
      state.sidebarOpened = !state.sidebarOpened
    }
  },
  actions: {}
}

在store/index.js中进行导入:

index.js 复制代码
import app from './modules/app'

export default createStore({
  getters,
  modules: {
    user,
    app
  }
})

在store/getters.js里创建一个快捷访问;

getters.js 复制代码
const getters = {
  ...
  sidebarOpened: (state) => state.app.sidebarOpened
}
export default getters

到这里我们数据源的问题就解决了;

现在来添加一个组件:

新建components/hamburger/index.js

index.js 复制代码
<template>
  <div class="hamburger-container" @click="toggleClick">
    <svg-icon class="hamburger" :icon="icon"></svg-icon>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'

const store = useStore()
// 点击事件
const toggleClick = () => {
  store.commit('app/triggerSidebarOpened')
}
const icon = computed(() => {
  return store.getters.sidebarOpened ? 'hamburger-opened' : 'hamburger-closed'
})
</script>

<style lang="scss" scoped>
.hamburger-container {
  height: 50px;
  padding: 0 20px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  /**hover 动画 */
  transition: backgrount 0.5s;

  &:hover {
    background: rgba(0, 0, 0, 0.1);
  }
}
</style>

在layout/components/Navbar.vue中导入组件:

Navbar.vue 复制代码
<template>
    <div class="navbar">
        <div class="left-menu">
          <Hamburger></Hamburger>
        </div>
    </div>
</template>

在components/Sidebar/SidebarMenu.vue中写入:

SidebarMenu.vue 复制代码
<template>
  <el-menu
    :collapse="!$store.getters.sidebarOpened">
  </el-menu>
<template>

Sidebar占位头像实现

在layout/components/Sidebar/index.vue中修改;

index.vue 复制代码
<template>
  <div>
    <div class="logo-container">
      <el-avatar
        size="44"
        shape="square"
        src="https://m.imooc.com/static/wap/static/common/img/[email protected]"
      ></el-avatar>
      <div class="logo-title" v-if="$store.getters.sidebarOpened">mj-admin</div>
    </div>
    <el-scrollerbar>
      <SidebarMenu />
    </el-scrollerbar>
  </div>
</template>
<script setup>
import SidebarMenu from './SidebarMenu.vue'
</script>
<style lang="scss" scoped>
@import '~@/styles/sidebar.scss';

.logo-container {
  height: 44px;
  padding: 10px 0 22px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  .logo-title {
    margin-left: 10px;
    color: #fff;
    font-weight: 600;
    font-size: large;
  }
  /**头像灰色背景处理 */
  .el-avatar {
    --el-avatar-bg-color: none;
    --el-avatar-background-color: none;
  }
}
</style>

在styles/variable.scss中定义一个transition时间常量;sidebar的transition还没加;

给sidebar加上动画效果

在variable.scss中增加一个常量:

variable.scss 复制代码
$sidebarDuration: 0.28s;
index.vue 复制代码
.sidebar-container {
  transition: width $sidebarDuration;
}
.main-container {
  transition: margin-left $sidebarDuration;
}
.hideSidebar{
  .fixed-header{
    transition: width $sidebarDuration;
  }
}
// menu菜单的border去掉
.el-menu {
  border: none;
}

补充知识点

什么是:v-deep?

scoped属性是HTML5中的新属性,<style标签加上了scoped>属性时,实现样式私有化模块化;

scoped会给DOM节点加一个不重复data属性来表示他的唯一性;

:deep()改变css解析时私有属性的位置;常用于修改组件库样式;

transition

transition属性可以用来实现动画效果;它有四个属性:

  • transition-property:指定CSS属性的name,transition效果;
  • transition-duration:指定多少秒多少毫秒才能完成;
  • transiontion-timing-function:指定transition效果的转速曲线;
  • transition-delay:定义transition效果开始的时候;

其他问题

  • 我在实现的时候发现样式总有问题,后来发现老师的 "element-plus": "^2.0.4",而我的是v1版本;
  • 还有折叠的样式,菜单折叠的时候总是显示出title;老师封装了一个MenuItem,我没用这个,直接改成,样式问题就解决了:
js 复制代码
<svg-icon :icon="route.meta.icon"></svg-icon>
<template #title>{{ route.meta.title }}</template>

组件状态驱动的动态css值

vue3.2更新中,还有一个很重要的更新,那就是组件状态驱动的动态css值 ,详情🔎

通过v-bind指令把响应式数据绑定在css上;

接下来我们就用v-bind为logo-container指定一下高度:

在Sidebar/index.vue中修改:

index.vue 复制代码
<template>
    <el-avatar
      :size="logoHeight">
    </el-avatar>
</template>
<script setup>
const logoHeight = 44
</script>
<style>
.logo-container {
  height: v-bind(logoHeight) + 'px';
}
</style>

动态面包屑

分成3个步骤来实现:

1.创建、渲染基本的面包屑组件;

2.计算面包屑结构属性;

3.根据数据渲染动态面包屑内容;

动态面包屑的实现

在Breadcrumb/index.vue中完整代码:

index.vue 复制代码
<template>
  <el-breadcrumb class="breadcrumb" separator="/">
    <el-breadcrumb-item
      v-for="(item, index) in breadcrumbData"
      :key="item.path"
    >
      <!--不可点击-->
      <span class="no-redirect" v-if="index === breadcrumbData.length - 1">{{
        item.meta.title
      }}</span>
      <!--可点击-->
      <span class="redirect" @click="onLinkClick(item)" v-else>{{
        item.meta.title
      }}</span>
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup>
import { watch, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'

// 生成数组数据
const breadcrumbData = ref([])
const getBreadcrumbData = () => {
  console.log(route.matched)
  breadcrumbData.value = route.matched.filter(
    (item) => item.meta && item.meta.title
  )
}

// 监听路由变化
const route = useRoute()
watch(
  route,
  () => {
    getBreadcrumbData()
  },
  {
    immediate: true
  }
)

// 跳转点击事件
const router = useRouter()
const onLinkClick = (item) => {
  router.push(item.path)
}

// 将来需要主题替换,所以hover的颜色我们设置为主色
const store = useStore()
const linkHoverColor = ref(store.getters.cssVar.menuBg)
</script>

<style lang="scss" scoped>
.breadcrumb {
  display: inline-block;
  font-size: 14px;
  line-height: 50px;
  margin-left: 8px;
  .no-redirect {
    color: #97a8be;
    cursor: text;
  }
  .redirect {
    cursor: pointer;
    color: #666;
    font-size: 14px;
  }
  .redirect:hover {
    color: v-bind(linkHoverColor);
  }
}
</style>

为面包屑增加动画样式

详情🔎

1.创建styles/index.scss

styles/index.scss 复制代码
.breadcrumb-enter-active,
.breadcrumb-leave-active {
  transition: all 0.5s;
}

.breadcrumb-enter-from,
.breadcrumb-leave-active {
  opacity: 0;
  transition: translageX(20px);
}

.breadcrumb-leave-active {
  position: absolute;
}

在Breadcrumb/index.vue多加一层transition;

index.vue 复制代码
<template>
  <el-breadcrumb class="breadcrumb" separator="/">
    <transition-group name="breadcrumb">
      <el-breadcrumb-item></el-breadcrumb-item>
    </transition-group>
  </el-breadcrumb>
</template>
相关推荐
工呈士几秒前
HTML与安全性:XSS、防御与最佳实践
前端·html·xss
WEI_Gaot4 分钟前
zustand 基础和进阶
前端·react.js
程序员Qian6 分钟前
从开发天气MCP服务入门什么是MCP
前端
用户2031196600968 分钟前
sheet的基本用法
前端
火星思想14 分钟前
都2025年了,还在问构建工具是干嘛的?
前端·前端框架·设计
杨进军17 分钟前
MutationObserver 实现 iframe 自适应高度
前端
火星思想18 分钟前
Promise 核心知识点(非基础)
前端·javascript·面试
前端大白话19 分钟前
炸裂!10个 React 实战技巧,让你的代码从“青铜”秒变“王者”
前端·javascript·react.js
Paramita19 分钟前
Koa源码解读
前端
用户614722537720319 分钟前
JavaScript 性能优化实战:从理论到落地的全面指南
前端