【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/logo-small@2x.png"
      ></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>
相关推荐
GIS程序媛—椰子16 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00122 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端25 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100929 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439139 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt