vue封裝adminlte3的左右布局

最近用vue封裝了一個adminlte3組件庫,已經封裝了差不多25個組件了扣墩。很多封裝adminlte的都是引入jq。扛吞。這個我有點無語呻惕。沒有歧視的意思,用jq還用什么vue滥比。而且vue中用jq會出現(xiàn)很多bug亚脆。
感興趣的老鐵可以進github看看,歡迎貢獻代碼盲泛。
歡迎加群交流 QQ群:927568606
nly-adminlte-vue

adminlte3 的左右布局注意是由body上的class來控制濒持。

  • 在控制左邊導航欄收縮展開的時候,有如下幾個css式樣
class="sidebar-mini" //允許左邊導航欄收起成只有圖標的形式
class="sidebar-collapse" // 收起左邊導航欄
class="sidebar-open"  // 小屏的時候展開左邊導航欄

wrapper

先封裝一個wrapper查乒,用來控制body的class弥喉。這個wrapper其實就是

<div class="wrapper >
   ...
</div>

由這個wrapper包裹其他所有元素÷昶可以看一下adminlte3的頁面結構


image.png

這個wrapper可以修改body的class由境,而且要監(jiān)聽窗口大小來修改body的class

把這wrapper命名為container-wrapper.js

import Vue from "../../../utils/vue";

const name = "NlyContainerWrapper";

export const NlyContainerWrapper = Vue.extend({
  name: name,
  props: {
    //邊側欄最小化
    sideMini: {
      type: Boolean,
      default: false
    },
    //layout fixed or boxed
    layout: {
      type: String
    },
    // navbar fixed
    navbarFixed: {
      type: Boolean,
      default: false
    },
    //footer fixed
    footerFixed: {
      type: Boolean,
      default: false
    },
    //top nav
    topNav: {
      type: Boolean,
      default: false
    },
    wrapperClass: {
      type: String
    },
    containerClass: {
      type: String
    }
  },
  computed: {
    sideMiniClass: function() {
      return this.sideMini ? "sidebar-mini" : "";
    },
    layoutClass: function() {
      return this.layout == "fixed"
        ? "layout-fixed"
        : this.layout
        ? "layout-boxed"
        : "";
    },
    navbarFixedClass: function() {
      return this.navbarFixed ? "layout-navbar-fixed" : "";
    },
    footerFixedClass: function() {
      return this.footerFixed ? "layout-footer-fixed" : "";
    },
    topNavClass: function() {
      return this.topNav ? "layout-top-nav" : "";
    },
    containerWrapperClass: function() {
      return this.wrapperClass;
    },
    containerBodyClass: function() {
      return this.containerClass;
    }
  },
  methods: {
    setBodyCollapseClassName() {
      if (this.sideMini) {
        const bodyWidth = document.body.clientWidth;
        const bodyClassName = document.body.className;

        if (bodyWidth < 992) {
          if (bodyClassName.indexOf("sidebar-collapse") == -1) {
            document.body.classList.add("sidebar-collapse");
          }
        } else {
          if (bodyClassName.indexOf("sidebar-open") !== -1) {
            document.body.classList.remove("sidebar-open");
          }
        }
      }
    },
    setBodyClassName(newval, oldval) {
      if (newval != oldval) {
        if (newval && oldval) {
          document.body.classList.add(newval);
          document.body.classList.remove(oldval);
        } else if (newval && oldval == "") {
          document.body.classList.add(newval);
        } else if (newval == "" && oldval) {
          document.body.classList.remove(oldval);
        }
      }
    }
  },
  mounted() {
    window.addEventListener(
      "resize",
      () => this.setBodyCollapseClassName(),
      false
    );
  },
  created() {
    const createdBodyClassList = [
      this.sideMiniClass,
      this.layoutClass,
      this.navbarFixedClass,
      this.footerFixed,
      this.topNavClass,
      this.containerBodyClass
    ];
    createdBodyClassList.forEach(item => {
      if (item) {
        document.body.classList.add(item);
      }
    });
    this.setBodyCollapseClassName();
  },
  beforeDestroy() {
    window.removeEventListener(
      "resize",
      this.setBodyCollapseClassName(),
      false
    );
  },
  watch: {
    sideMiniClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    layoutClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    navbarFixedClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    footerFixedClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    topNavClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    containerBodyClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    },
    containerWrapperClass: function(newval, oldval) {
      this.setBodyClassName(newval, oldval);
    }
  },
  render(h) {
    return h(
      "div",
      {
        staticClass: "wrapper",
        class: [this.containerWrapperClass]
      },
      this.$slots.default
    );
  }
});

content-wrapper組件就出來了。
主要props如下:

參數 類型 默認值 描述
side-mini Boolean 邊側欄是否可以收起,true可以收起,false將邊側畫板左側滑入消失
layout String 整體布局,可選fixed和boxed
navbar-fixed Boolean 頭部導航fixed布局
footer-fixed Boolean 底部fixed布局
top-nav Boolean 頭部導航頂格無邊側欄布局
warpper-class String wrapper 式樣
container-class String body式樣

v-nly-sidebar-collapse

再封裝一個指令,用來控制body class的修改虏杰。
v-nly-sidebar-collapse這個指令就用來控制左側收起展開的讥蟆。在綁定這個指令的元素上點擊就會觸發(fā)對應事件。

指令組件sidebar-collapse.js

import {
  navItemOenEvent,
  navItemCollapseEvent,
  setInstanceAttr,
  overLayCollapseEvent
} from "../../utils/sidebar-collapse";

export const NlySidebarCollapse = {
  bind(el, binding, vnode) {
    const instanceNameList = Object.keys(binding.modifiers);
    if (instanceNameList.indexOf("navitem") != -1) {
      window.addEventListener("resize", () => setInstanceAttr(vnode), false);
    }
    el.onclick = function() {
      if (instanceNameList.indexOf("navitem") != -1) {
        const bodyWidth = document.body.clientWidth;
        if (bodyWidth < 992) {
          navItemOenEvent();
        } else {
          navItemCollapseEvent();
        }
      }
      if (instanceNameList.indexOf("overlay") != -1) {
        overLayCollapseEvent();
      }
    };
  }
  //   unbind(el, vnode, binding) {
  //     console.log(el, vnode, binding);
  //     const instanceNameList = Object.keys(binding.modifiers);
  //     if (instanceNameList.indexOf("navitem") != -1) {
  //       Window.removeEventListener("resize", () => setInstanceAttr(vnode), false);
  //     }
  //   }
};

utils文件夾下的sidebar-collapse.js
utils/sidebar-collapse.js

import { setAttr } from "./dom";

export const eventType = {
  collapse: "sidebar-collapse",
  open: "sidebar-open",
  show: "control-sidebar-slide-open",
  animate: "control-sidebar-animate"
};

export const selector = {
  controlSidebar: ".control-sidebar",
  header: ".main-header",
  footer: ".main-footer",
  controlSidebarContent: ".control-sidebar-content"
};

export const getSelector = cls => {
  return document.querySelector(cls);
};

export const getBodyClassName = () => {
  return document.body.className;
};

export const getBodyWidth = () => {
  return document.body.clientWidth;
};

export const navItemOenEvent = () => {
  const bodyClassName = getBodyClassName();
  if (bodyClassName.indexOf(eventType.collapse) == -1) {
    if (bodyClassName.indexOf(eventType.open) == -1) {
      document.body.classList.add(eventType.collapse);
    } else {
      document.body.classList.remove(eventType.open);
      document.body.classList.add(eventType.collapse);
    }
  } else {
    document.body.classList.add(eventType.open);
    document.body.classList.remove(eventType.collapse);
  }
};

export const navItemCollapseEvent = () => {
  const bodyClassName = getBodyClassName();
  if (bodyClassName.indexOf(eventType.collapse) == -1) {
    document.body.classList.add(eventType.collapse);
  } else {
    document.body.classList.remove(eventType.collapse);
  }
};

export const setInstanceAttr = vnode => {
  const bodyWidth = getBodyWidth();
  if (bodyWidth < 992) {
    setAttr(vnode.children[0].elm, "data-widget", eventType.open);
  } else {
    setAttr(vnode.children[0].elm, "data-widget", eventType.collapse);
  }
};

export const overLayCollapseEvent = () => {
  const bodyClassName = getBodyClassName();
  if (bodyClassName.indexOf(eventType.collapse) == -1) {
    document.body.classList.remove(eventType.open);
    document.body.classList.add(eventType.collapse);
  }
};

export const getScrollTop = () => {
  return document.documentElement && document.documentElement.scrollTop
    ? document.documentElement.scrollTop
    : document.body
    ? document.body.scrollTop
    : 0;
};

export const getScrollHeight = () => {
  return document.documentElement && document.documentElement.scrollHeight
    ? document.documentElement.scrollHeight
    : document.body
    ? document.body.scrollHeight
    : 0;
};

export const getBodyOffsetHeight = () => {
  return document.documentElement && document.documentElement.offsetHeight
    ? document.documentElement.offsetHeight
    : document.body
    ? document.body.offsetHeight
    : 0;
};
/**
 * 展開先給html添加class='control-sidebar-animate'
 * 設置control-sidebar display='block'
 * 10ms之后給body添加class='control-sidebar-slide-open'
 * 300ms之后給html刪除class='control-sidebar-animate'
 *
 * 收起先給html添加class='control-sidebar-animate'
 * 刪除body class='control-sidebar-slide-open
 * 300ms之后設置control-sidebar display='none'
 * html刪除class='control-sidebar-animate'
 * @param {s} vnode
 */

// 10ms之后給body添加class='control-sidebar-slide-open'
export const openAddBodyClass = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      document.body.classList.add(eventType.show);
      resolve();
    }, 10);
  });
};
// 300ms之后給html刪除class='control-sidebar-animate'
export const openRemoveHtmlClass = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      getSelector("html").classList.remove(eventType.animate);
      resolve();
    }, 300);
  });
};

// 300ms之后設置control-sidebar display='none'
export const collapseSetAttrsDisplay = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      getSelector(selector.controlSidebar).style.display = "none";
      resolve();
    }, 300);
  });
};

// html刪除class='control-sidebar-animate'
export const collapseRemoveHtmlClass = () => {
  getSelector("html").classList.remove(eventType.animate);
};

// 隊列執(zhí)行open操作
export async function openTasks() {
  await openAddBodyClass();
  await openRemoveHtmlClass();
}

// 隊列執(zhí)行collapse操作
export async function collapseTasks() {
  await collapseSetAttrsDisplay();
  await collapseRemoveHtmlClass();
}

// 監(jiān)聽header纺阔,footer高度以及滾動條高度瘸彤,給control-siderbar設置height
export const setControlSidebarStyle = () => {
  const windowHeight = document.documentElement.clientHeight;
  const bodyHeight = getBodyOffsetHeight();
  const scrollTop = getScrollTop();
  const scrollHeight = getScrollHeight();
  const headerHeight = getSelector(selector.header).offsetHeight;
  const footerHeight = getSelector(selector.footer).offsetHeight;
  // console.log(11, bodyHeight);
  // console.log(22, scrollTop);
  // console.log(33, scrollHeight);
  // console.log(44, windowHeight);
  // console.log(
  //   55,
  //   footerHeight -
  //     scrollHeight +
  //     windowHeight -
  //     headerHeight +
  //     scrollTop +
  //     footerHeight
  // );
  const controlSidebarSelector = getSelector(selector.controlSidebar);
  const controlSidebarContentSelector = getSelector(
    selector.controlSidebarContent
  );

  if (scrollTop < headerHeight) {
    if (bodyHeight - windowHeight >= footerHeight) {
      controlSidebarSelector.style.top = `${headerHeight - scrollTop}px`;
      if (
        footerHeight -
          scrollHeight +
          windowHeight -
          headerHeight +
          scrollTop +
          footerHeight >
        0
      ) {
        controlSidebarSelector.style.height = `${scrollHeight -
          footerHeight -
          footerHeight}px`;
        controlSidebarContentSelector.style.height = `${scrollHeight -
          footerHeight -
          footerHeight}px`;
      } else {
        controlSidebarSelector.style.height = `${windowHeight -
          headerHeight +
          scrollTop}px`;
        controlSidebarContentSelector.style.height = `${windowHeight -
          headerHeight +
          scrollTop}px`;
      }
    } else {
      controlSidebarSelector.style.top = `${headerHeight - scrollTop}px`;
      controlSidebarSelector.style.height = `${bodyHeight -
        headerHeight -
        footerHeight}px`;
      controlSidebarContentSelector.style.height = `${bodyHeight -
        headerHeight -
        footerHeight}px`;
      controlSidebarSelector.style.bottom = `${footerHeight -
        bodyHeight +
        windowHeight +
        scrollTop}px`;
    }
  } else {
    controlSidebarSelector.style.top = "0px";
    if (scrollHeight - windowHeight - scrollTop <= footerHeight) {
      controlSidebarSelector.style.height = `${scrollHeight -
        footerHeight -
        scrollTop}px`;
      controlSidebarContentSelector.style.height = `${scrollHeight -
        footerHeight -
        scrollTop}px`;
      controlSidebarSelector.style.bottom = `${windowHeight +
        scrollTop +
        footerHeight -
        scrollHeight}px`;
    } else if (scrollHeight - windowHeight - scrollTop > footerHeight) {
      controlSidebarSelector.style.height = `${windowHeight}px`;
      controlSidebarContentSelector.style.height = `${windowHeight}px`;
    }
  }
};

// 展開 control-sidebar
export const controlSidebarOpen = () => {
  getSelector("html").classList.add(eventType.animate);
  getSelector(selector.controlSidebar).style.display = "block";
  openTasks();
  window.addEventListener("scroll", setControlSidebarStyle, false);
  window.addEventListener("resize", setControlSidebarStyle, false);
};

// 收起 control-sidebar
export const controlSidebarCollapse = () => {
  getSelector("html").classList.add(eventType.animate);
  document.body.classList.remove(eventType.show);
  collapseTasks();
  window.removeEventListener("scroll", setControlSidebarStyle, false);
  window.removeEventListener("resize", setControlSidebarStyle, false);
};

export const controlSidebarShow = () => {
  const bodyClassName = getBodyClassName();
  if (bodyClassName.indexOf(eventType.show) == -1) {
    controlSidebarOpen();
  } else {
    //收起
    controlSidebarCollapse();
  }
};

這時候組件跟指令都寫好了,注冊組件跟指令笛钝,然后把對應的代碼替換掉质况,就能實現(xiàn)左側收起展開了。

效果圖


nly-adminlte-vue-2.gif
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末玻靡,一起剝皮案震驚了整個濱河市结榄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囤捻,老刑警劉巖臼朗,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蝎土,居然都是意外死亡视哑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門誊涯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挡毅,“玉大人,你說我怎么就攤上這事暴构】妒龋” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵丹壕,是天一觀的道長。 經常有香客問我薇溃,道長菌赖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任沐序,我火速辦了婚禮琉用,結果婚禮上,老公的妹妹穿的比我還像新娘策幼。我一直安慰自己邑时,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布特姐。 她就那樣靜靜地躺著晶丘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浅浮,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天沫浆,我揣著相機與錄音,去河邊找鬼滚秩。 笑死专执,一個胖子當著我的面吹牛,可吹牛的內容都是我干的郁油。 我是一名探鬼主播本股,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桐腌!你這毒婦竟也來了拄显?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤哩掺,失蹤者是張志新(化名)和其女友劉穎凿叠,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體嚼吞,經...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡盒件,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了舱禽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炒刁。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖誊稚,靈堂內的尸體忽然破棺而出翔始,到底是詐尸還是另有隱情,我是刑警寧澤里伯,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布城瞎,位于F島的核電站,受9級特大地震影響疾瓮,放射性物質發(fā)生泄漏脖镀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一狼电、第九天 我趴在偏房一處隱蔽的房頂上張望蜒灰。 院中可真熱鬧,春花似錦肩碟、人聲如沸强窖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翅溺。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間未巫,已是汗流浹背窿撬。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叙凡,地道東北人劈伴。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像握爷,于是被迫代替她去往敵國和親跛璧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內容

  • 前端開發(fā)面試題 面試題目: 根據你的等級和職位的變化新啼,入門級到專家級追城,廣度和深度都會有所增加。 題目類型: 理論知...
    怡寶丶閱讀 2,572評論 0 7
  • @轉自GitHub 介紹一下標準的CSS的盒子模型燥撞?低版本IE的盒子模型有什么不同的座柱?(1)有兩種, IE 盒子模...
    YT_Zou閱讀 1,309評論 0 1
  • CSS命名規(guī)則 頭:header內容:content/containe尾:footer導航:nav側欄:sideb...
    紋小艾閱讀 732評論 0 9
  • CSS命名規(guī)則 頭:header 內容:content/containe 尾:footer 導航:nav 側欄:s...
    建昕82閱讀 734評論 0 6
  • CSS命名規(guī)則 頭:header 內容:content/containe 尾:footer 導航:nav 側欄:s...
    王小傲閱讀 1,500評論 0 9