专栏首页CRPER折腾记Vue折腾记 - (1)写一个不大靠谱的二级侧边栏

Vue折腾记 - (1)写一个不大靠谱的二级侧边栏

前言

本来想写个新手系列教程..发现这种东西一搜索一大把; 那就写点实战类的吧;这篇文章你能学点什么? 当然是一些常见内置指令的用法,组件过渡,遍历的思路等等


效果图

实现思路

  • 过渡用css -
  • 遍历循环判断(比对路由,点击的项名等)
  • 增加标记位来默认展开刷新页面当前所在项
  • 尽量减少DOM的改动,能用v-show的区域绝不用v-if
  • 自定义菜单JSON尽量格式简洁,没有一大坨标记位这些干扰物(越简单越方便后期)

代码

  • 菜单menuList.js
export const MENULIST = [
  {
    menuName: "客户管理",
    menuIcon: "fz-ad-icon-test",
    menuSubLink: [
      {
        menuName: "广告主",
        menuUrl: "/customer/adhost"
      },
      {
        menuName: "渠道",
        menuUrl: "/customer/channel"
      }
    ]
  },
  {
    menuName: "广告管理",
    menuIcon: "fz-ad-guanggao",
    menuSubLink: [
      {
        menuName: "广告新增",
        menuUrl: "/ad/add"
      },
      {
        menuName: "广告审核",
        menuUrl: "/ad/check"
      }
    ]
  },
  {
    menuName: "投放管理",
    menuIcon: "fz-ad-toufang",
    menuSubLink: [
      {
        menuName: "广告位",
        menuUrl: "/puton/area"
      },
      {
        menuName: "广告规格",
        menuUrl: "/puton/regular"
      }
    ]
  },
  {
    menuName: "数据统计",
    menuIcon: "fz-ad-statistics",
    menuSubLink: [
      {
        menuName: "广告主",
        menuUrl: "/status/adhost"
      },
      {
        menuName: "渠道",
        menuUrl: "/status/channel"
      }
    ]
  },
  {
    menuName: "管理者",
    menuIcon: "fz-ad-guanli",
    menuUrl: "/manager"
  },
  {
    menuName: "操作日志",
    menuIcon: "fz-ad-rizhi",
    menuUrl: "/logger"
  }
];
  • Sidebar.vue
<template>
  <div class="sidebar" >
    <ul class="sidebar-menu ">`
      <template v-for="(item ,index) in menulist">
        <li :class="currentUrl === item.menuUrl ? 'active':''">
          <template v-if="item.menuUrl">
            <router-link :to="item.menuUrl" @click.native="toggleName=''">
              <i :class="['fzicon',item.menuIcon]"></i>
              <span>{{item.menuName}}</span>
            </router-link>
          </template>
          <template v-else-if="item.menuSubLink">
            <a href="javascript:;" @click="isToggle(item.menuName,item.defaultActive)">
              <i :class="['fzicon',item.menuIcon]"></i>
              <span>{{item.menuName}}</span>
              <i class="trangle" :class="[config.iconfont, (item.menuName === toggleName) || item.defaultActive? config.icon_expand: config.icon_collapse]">
              </i>
            </a>
            <transition name="sliderToggle" mode="out-in">
              <ul class="tree-menu" v-show="item.menuName === toggleName || item.defaultActive">
                <li v-for="(subitem,subindex) in item.menuSubLink" :key="subitem" :class="currentUrl === subitem.menuUrl ? 'active':''">
                  <router-link :to="subitem.menuUrl">
                    <i :class="subitem.menuIcon"></i>
                    <span>{{subitem.menuName}}</span>
                  </router-link>
                </li>
              </ul>
            </transition>
          </template>
        </li>
      </template>
    </ul>
  </div>
</template>

<script>
import { MENULIST } from './menuList'; // 引入的自定义菜单数据
export default {
  name: 'layout-sidebar',
  data: function () {
    return {
      menulist: MENULIST, // 自定义菜单数据
      currentUrl: '', // 当前浏览器的url
      toggleName: '',  // 菜单子项目名称
      config: {
        'iconfont': 'fzicon', // iconfont的字体
        'icon_collapse': 'fz-ad-jiantou', // 箭头
        'icon_expand': 'fz-ad-jiantou1' // 箭头
      }
    }
  },
  props: ['toggle', 'padMode'], // 这里是用来构成布局响应传递的props,单一组件不用管他
  watch: {
    '$route'() {
      this.currentUrl = this.$route.fullPath; // 实时监测当前路由的变化并且赋值
    }

  },
  methods: {
    isToggle(name, defaultActive) {
      this.clearDefaultActive(); // 清除标记位,是否当前为默认展开
      defaultActive ? false : name !== this.toggleName ? this.toggleName = name : this.toggleName = ''; // 判断展开收缩的核心
    },
    clearDefaultActive() {
      this.menulist.forEach(item => {
        this.$delete(item, 'defaultActive')
      })
    }
  },
  created: function () {
    this.currentUrl = this.$route.fullPath;
    this.$nextTick(() => {
      this.menulist.forEach((item, index) => { // 增加标记位,判断当前url然后自动展开或者激活对应项(刷新默认展开当前url的项)
        if (!item.menuSubLink && item.menuUrl) {
          this.currentUrl === item.menuUrl ? this.$set(item, 'defaultActive', true) : '';
        } else {
          if (item.menuSubLink) {
            item.menuSubLink.forEach((subitem, index) => {
              this.currentUrl === subitem.menuUrl ? this.$set(item, 'defaultActive', true) : '';
            })
          }
        }
      })
    })
  },
  mounted: function () {
  }
}
</script>

<style scoped lang="scss">
// 自定义过渡效果
.sliderToggle-enter-active,
.sliderToggle-leave-active {
  transition: all 0.5s linear;
  height: 100%;
  height: auto;

  overflow: hidden;
}

.sliderToggle-enter,
.sliderToggle-leave-to {
  overflow: hidden;
  padding-top: 0;
  padding-bottom: 0;
  height: 0;
  opacity: 0;
}

// 侧边栏全局样式
.sidebar {
  position: absolute;
  top: 0;
  left: 0;
  padding-top: 68px;
  min-height: 100%;
  z-index: 810;
  transition: all 0.3s linear;
  background-color: #222d32;
  .sidebar-menu {
    list-style: none;
    margin: 0;
    padding: 0;
    white-space: nowrap;
    span {
      cursor: pointer;
    }
    a {
      text-decoration: none;
      color: #8aa4af;
      &:hover {
        color: #fff;
      }
    }
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
      white-space: nowrap;
      overflow: hidden;
    }
    >li {
      position: relative;
      margin: 0;
      padding: 0;
      text-align: left;
      &.active {
        color: #fff;
      }
      >a {
        border-left: 3px solid transparent;
        position: relative;
        padding: 15px 5px 15px 19px;
        display: block;
        font-size: 14px;
        >i {
          padding-right: 4px;
        }
        .trangle {
          float: right;
          padding-right: 10px;
        }
      }
      &.active {
        >a {
          border-left: 3px solid #3c8dbc;
          color: #fff;
        }
      }
      >.tree-menu {
        margin: 0 1px;
        background: #2c3b41;
        list-style: none;
        padding: 0;
        margin: 0;
        .tree-menu {
          padding-left: 20px;
        }
        >li {
          margin: 0;
          &.active {
            >a {
              border-left: 3px solid #3c8dbc;
              color: #fff;
            }
          }
          >a {
            padding: 15px 10px 15px 40px;
            display: block;
            font-size: 14px;
            border-left: 3px solid transparent;
          }
        }
        &.active {
          display: block;
        }
      }
    }
  }

  &.expand {
    width: 230px;
  }

  &.collapse {
    width: 50px;
    .sidebar-menu {
      >li {
        >a {
          padding: 15px 5px 15px 15px;
          span,
          i:last-child {
            display: none;
          }
        }
        .tree-menu {
          display: none;
        }
        &:hover {
          >a {
            display: block;
            span {
              display: block;
              position: absolute;
              left: 47px;
              width: 153px;
              padding: 15px 5px 15px 19px;
              background-color: #222d32;
              color: #fff;
              width: 177px;
              top: 0;
              border-radius: 0 5px 0 0;
            }
            i:last-child {
              display: inline-block;
              position: absolute;
              right: -165px;
              top: 50%;
              transform: translateY(-50%);
              color: #fff;
            }
          }
          >.tree-menu {
            display: block;
            position: absolute;
            left: 47px;
            width: 180px;
            background-color: #222d32;
            color: #fff;
            top: 46px;
            width: 180px;
            border-radius: 0 0 5px 5px;
          }
        }
      }
    }
  }
}
</style>

总结

实际上这货就是把adminlte的风格用vue实现了... 还有变形金刚版本,,整个响应涉及几个到组件间的通讯; 就不把全部代码丢出来了,一大坨...

效果是这样的

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React 折腾记 - (7) 基于React+Antd封装聊天记录(用到React的memo,lazy, Suspense这些)

    CRPER
  • JS题目之数组数据拆分重组转成嵌套对象,让脑细胞活跃下

    CRPER
  • Angular 2 + 折腾记 :(2)初步认识angular2,不一样的开发模式

    想来想去,概念这些东西不怎么想讲,更多的是想讲点实战性的内容。 所以有些东西跳过去了,小伙伴们请去看官方文档哈;跳跃性的前进,写的不好多包涵。。。

    CRPER
  • CSS学习笔记(基础篇)

    CSS 指层叠样式表 (Cascading Style Sheets)(级联样式表)

    Daotin
  • 一张图,搞定大数据工程师的成长路径

    上面这几种情况,一看就是没经历过真实项目,也没有受过系统训练导致的,我们做大数据的薪酬是高,但门槛也是高,因为不管你是什么级别,所需要的技术栈你都应该用过,否则...

    王知无
  • RN性能优化(重新探索react吧)

    <!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 19.0px; font: 13.0px 'H...

    windseek
  • 【程序员装B系列】八种CSS3按钮动画特效

    这是一款CSS3按钮动画特效。在该特效中,提供了八种按钮动画特效。每种特效在鼠标悬停到按钮上面的时候,都会触发按钮动画。

    用户5997198
  • 使用Spring Boot实现模块化

    lyb-geek
  • Vue框架Element UI教程-出现和隐藏动画(三)

    今天继续写组件的运用相关例子 点击按钮的时候,元素会呈现一个出现和隐藏的动画功能。

    王小婷
  • Ajax-数据异步交互1.Ajax简介2.AJAX 实例

    AJAX = 异步 JavaScript 和 XML。 AJAX 是一种用于创建快速动态网页的技术。 通过在后台与服务器进行少量数据交换,AJAX 可以使网...

    意气相许的许

扫码关注云+社区

领取腾讯云代金券