Vue折騰記 - (1)寫一個不大靠譜的二級側邊欄

CRPER發表於2017-07-14

前言

本來想寫個新手系列教程..發現這種東西一搜尋一大把;
那就寫點實戰類的吧;這篇文章你能學點什麼?
當然是一些常見內建指令的用法,元件過渡,遍歷的思路等等


效果圖

實現思路

  • 過渡用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實現了...
還有變形金剛版本,,整個響應涉及幾個到元件間的通訊;
就不把全部程式碼丟出來了,一大坨...

效果是這樣的

相關文章