vue封裝adminlte3的左右布局

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

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

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

wrapper

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

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

由這個wrapper包裹其他所有元素??梢钥匆幌耡dminlte3的頁面結(jié)構(gòu)


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: {
    //邊側(cè)欄最小化
    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如下:

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

v-nly-sidebar-collapse

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

指令組件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'
 * 設(shè)置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之后設(shè)置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之后設(shè)置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設(shè)置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();
  }
};

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

效果圖


nly-adminlte-vue-2.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前端開發(fā)面試題 面試題目: 根據(jù)你的等級和職位的變化,入門級到專家級,廣度和深度都會有所增加。 題目類型: 理論知...
    怡寶丶閱讀 2,683評論 0 7
  • @轉(zhuǎn)自GitHub 介紹一下標(biāo)準(zhǔn)的CSS的盒子模型?低版本IE的盒子模型有什么不同的?(1)有兩種, IE 盒子模...
    YT_Zou閱讀 1,383評論 0 1
  • CSS命名規(guī)則 頭:header內(nèi)容:content/containe尾:footer導(dǎo)航:nav側(cè)欄:sideb...
    紋小艾閱讀 774評論 0 9
  • CSS命名規(guī)則 頭:header 內(nèi)容:content/containe 尾:footer 導(dǎo)航:nav 側(cè)欄:s...
    建昕82閱讀 781評論 0 6
  • CSS命名規(guī)則 頭:header 內(nèi)容:content/containe 尾:footer 導(dǎo)航:nav 側(cè)欄:s...
    王小傲閱讀 1,538評論 0 9

友情鏈接更多精彩內(nèi)容