【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>
相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试