美文网首页Vue cli3 学习
vue封装adminlte3的左右布局

vue封装adminlte3的左右布局

作者: 一颗数据小白菜 | 来源:发表于2020-03-12 00:14 被阅读0次

    最近用vue封装了一个adminlte3组件库,已经封装了差不多25个组件了。很多封装adminlte的都是引入jq。。这个我有点无语。没有歧视的意思,用jq还用什么vue。而且vue中用jq会出现很多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,而且要监听窗口大小来修改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这个指令就用来控制左侧收起展开的。在绑定这个指令的元素上点击就会触发对应事件。

    指令组件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);
    };
    
    // 队列执行open操作
    export async function openTasks() {
      await openAddBodyClass();
      await openRemoveHtmlClass();
    }
    
    // 队列执行collapse操作
    export async function collapseTasks() {
      await collapseSetAttrsDisplay();
      await collapseRemoveHtmlClass();
    }
    
    // 监听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();
      }
    };
    

    这时候组件跟指令都写好了,注册组件跟指令,然后把对应的代码替换掉,就能实现左侧收起展开了。

    效果图


    nly-adminlte-vue-2.gif

    相关文章

      网友评论

        本文标题:vue封装adminlte3的左右布局

        本文链接:https://www.haomeiwen.com/subject/oszkjhtx.html