美文网首页
Vue 封装 Adminle3 左右上中下布局

Vue 封装 Adminle3 左右上中下布局

作者: 一颗数据小白菜 | 来源:发表于2020-04-18 18:30 被阅读0次

    为了响应式研究了一下 adminlte3,封装了一个 Adminlte Vue 组件库。名字叫 nly-adminlte-vue。

    GitHub 地址:nly-adminlte-vue
    文档地址: nly-adminlte-vue-docs
    在线 Demo 地址: nly-daminlte-vue-demo

    文档目前还只写完了 5 个组件文档。其他的都在每个组件下的 README.md 中。

    组件库效果


    nly-adminlte-vue-1.gif

    nly-adminlte-vue 全是 jsx 封装的组件,大部分是函数式组件,渲染速度是要比.vue 组件快。

    这个组件库没有引入 jq。注意没有引入 JQ。很多 Adminlte Vue 的组件库都是引入了 JQ,这样肯定是不行的,多少会有 bug,至于为什么不行,请理解 Vue 的精髓, 数据驱动 目前差不多快 50 个复用组件了。

    adminlte3 的布局有一种 collapse 布局,也就是左边侧导航栏,右侧上中下布局,且边侧导航栏可以收起展开。

    admintle3 控制布局的 class 主要在<body>标签上。左右布局中,主要有 3 个 css 类来控制布局。这三个 css 类主要在左右布局中使用。

    • 第一个是 layout-fixed,这个主要是控制右侧上中下布局在初始情况下有一个 margin-left: 250px 的属性,即偏移左侧 250px,腾出空间给左侧。且还有一个作用是让左侧导航栏滚动条与窗口滚动条不冲突。

    • 第二个是 sidebar-mini,这个允许左侧导航栏在设置好的断点状态能收起到边侧只显示图标。如果没有这个,边侧导航栏收起会直接消失,不出现在窗口中。

    • 第三个是 sidebar-collapse。实际上应该还有一个 sidebar-open。不过 sidebar-open 是在小屏情况下,展开左侧导航栏才会出现。sidebar-collapse 是用来控制左侧导航栏收起的 css 类。

    组件

    分解一下组件。

    • 容器:容器是用来包裹左侧导航栏,右侧上中下布局的容器。常见的有 container。但是 adminlte 中是用 wrapper。给组件去一个名字叫 NlyWrapper
      <br />
    • 左侧导航栏容器:左侧导航栏容器是一个包裹左侧导航栏面板的容器。给组件取一个名字叫 NlyWrapperSidbar
      <br />
    • 右侧 header 容器:右侧布局上的容器,通常可以包裹 navbar 菜单等。给组件取一个名字叫 NlyWrapperHeader
      <br />
    • 右侧 main 容器:包裹页面正文内容的容器。给组件取一个名字叫 NlyWrapperContent
      <br />
    • 右侧 footer 容器:包裹右侧布局底部一些内容。给组件取一个名字叫 NlyWrapperFooter

    这时候布局代码就应该是这样

    <template>
        <nly-wrapper>
            <nly-wrapper-header> header</nly-wrapper-header>
            <nly-wrapper-sidebar> left </nly-wrapper-sidebar>
            <nly-wrapper-content>
                main
            </nly-wrapper-content>
            <nly-wrapper-footer> footer</nly-wrapper-footer>
        </nly-wrapper>
    </template>
    

    NlyWrapper

    这是一个容器组件, 这个组件主要用来包裹其他所有组件,且用来控制 body 的 css 类。

    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式样
    import Vue from '../../utils/vue'
    
    const name = 'NlyWrapper'
    
    export const NlyWrapper = Vue.extend({
        name: name,
        props: {
            sideMini: {
                type: Boolean,
                default: false,
            },
            layout: {
                type: String,
            },
            navbarFixed: {
                type: Boolean,
                default: false,
            },
            footerFixed: {
                type: Boolean,
                default: false,
            },
            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
            )
        },
    })
    

    NlyWrapperSidebar

    这是一个容器组件, 主要用来包裹左侧导航栏的

    props 如下

    参数 类型 默认值 描述
    variant String dark-primary 导航栏主题色
    hover Boolean true 导航栏收起时,鼠标悬浮展开,设置为false则无悬浮效果
    elevation String 导航栏阴影,可选,sm,md,lg,xl。依次增大
    import Vue from '../../utils/vue'
    
    import { nlyGetOptionsByKeyEqual } from '../../utils/get-options'
    
    import {
        sidebarElevationOptions,
        sidebarContainerVariantOpitons,
    } from '../../utils/nly-config'
    
    const name = 'NlyWrapperSidebar'
    
    export const NlyWrapperSidebar = Vue.extend({
        name: name,
        props: {
            variant: {
                type: String,
                default: 'darkPrimary',
            },
            hover: {
                type: Boolean,
                default: true,
            },
            elevation: {
                type: String,
                default: 'xl',
            },
            tag: {
                type: String,
                default: 'aside',
            },
        },
        computed: {
            customVariant: function () {
                return nlyGetOptionsByKeyEqual(
                    sidebarContainerVariantOpitons,
                    this.variant
                )
            },
            customHover: function () {
                return this.hover ? '' : 'sidebar-no-expand'
            },
            customElevation: function () {
                return nlyGetOptionsByKeyEqual(
                    sidebarElevationOptions,
                    this.elevation
                )
            },
            customTag: function () {
                return this.tag
            },
        },
        render(h) {
            return h(
                this.customTag,
                {
                    staticClass: 'main-sidebar',
                    class: [
                        this.customVariant,
                        this.customElevation,
                        this.customHover,
                    ],
                },
                this.$slots.default
            )
        },
    })
    

    NlyWrapperHeader

    这是一个容器组件, 主要用来包裹右侧 header 的。在这个组件中,有几个 prop 是在 prop nav=true 的时候生效,这是因为在 adminlte3 中,navbar 直接就做成了顶部容器。所有给了一个 nav props 让 header 容器可以变成 navbar

    props 如下

    参数 类型 默认值 描述
    expand String navbar-expand 菜单栏屏幕变化收起断点,默认是sm断点,可选xl,lg,md,sm,no
    variant String white 菜单主题颜色,可选primary,secondary,success,info,warning,danger,lightblue,navy,olive,lime,fuchsia,maroon,blue,indigo,purple,pink,red,orange,yellow,green,teal,cyan,white,gray,graydark
    dark Boolean false 字体颜色,默认是黑色,设置true为白色
    size String 菜单字体大小,可选sm,lg
    boder Boolean true 菜单底边框,header为true的时候生效
    navbar-class String 自定义css式样
    import Vue from '../../utils/vue'
    import { nlyGetOptionsByKeyEqual } from '../../utils/get-options'
    import { navbarVariantOpitons, textSizeOptions } from '../../utils/nly-config'
    
    const name = 'NlyWrapperHeader'
    
    export const NlyWrapperHeader = Vue.extend({
        name: name,
        props: {
            expand: {
                type: String,
            },
            variant: {
                type: String,
                default: 'white',
            },
            dark: {
                type: Boolean,
                default: false,
            },
            size: {
                type: String,
                default: '',
            },
            border: {
                type: Boolean,
                default: true,
            },
            wrapperHeaderClass: {
                type: String,
            },
            tag: {
                type: String,
                default: 'header',
            },
            nav: {
                type: Boolean,
                default: false,
            },
        },
        computed: {
            customTag() {
                return this.tag
            },
            customNav() {
                return this.nav ? 'navbar' : ''
            },
            customNavbarExpand: function () {
                if (this.nav) {
                    return this.expand == 'xl'
                        ? 'navbar-expand-xl'
                        : this.expand == 'lg'
                        ? 'navbar-expand-lg'
                        : this.expand == 'md'
                        ? 'navbar-expand-md'
                        : this.expand == 'sm'
                        ? 'navbar-expand-sm'
                        : this.expand == 'no'
                        ? 'navbar-no-expand'
                        : this.expand == 'expand'
                        ? 'navbar-expand'
                        : ''
                } else {
                    return ''
                }
            },
            customnNvbarVariant: function () {
                if (this.nav) {
                    return nlyGetOptionsByKeyEqual(
                        navbarVariantOpitons,
                        this.variant
                    )
                } else {
                    return ''
                }
            },
            customNavbarFontSize: function () {
                if (this.nav) {
                    return nlyGetOptionsByKeyEqual(textSizeOptions, this.size)
                } else {
                    return ''
                }
            },
            customNavbarBorder: function () {
                if (this.nav) {
                    return this.border ? '' : 'border-bottom-0'
                } else {
                    return ''
                }
            },
            customWrapperHeaderClass: function () {
                return this.wrapperHeaderClass
            },
            customNavbarDark() {
                if (this.nav) {
                    return this.dark ? 'navbar-dark' : 'navbar-light'
                } else {
                    return ''
                }
            },
        },
        render(h) {
            return h(
                this.customTag,
                {
                    staticClass: 'main-header',
                    class: [
                        this.customNavbarExpand,
                        this.customNavbarDark,
                        this.customnNvbarVariant,
                        this.customNavbarFontSize,
                        this.customNavbarBorder,
                        this.customWrapperHeaderClass,
                    ],
                },
                this.$slots.default
            )
        },
    })
    

    NlyWrapperContent

    这是一个容器组件, 主要用来包裹右侧 main 的 的。

    props 如下

    参数 类型 默认值 描述
    tag String div 标签
    import Vue from '../../utils/vue'
    
    const name = 'NlyWrapperContent'
    
    export const NlyWrapperContent = Vue.extend({
        name: name,
        props: {
            tag: {
                type: String,
                default: 'div',
            },
        },
        computed: {
            customProps() {
                return {
                    tag: this.tag,
                }
            },
        },
        render(h) {
            return h(
                this.customProps.tag,
                {
                    staticClass: 'content-wrapper',
                },
                this.$slots.default
            )
        },
    })
    

    NlyWrapperFooter

    这是一个容器组件, 主要用来包裹右侧 footer 的 的。

    props 如下

    参数 类型 默认值 描述
    size String 文字大小
    import Vue from '../../utils/vue'
    
    const name = 'NlyWrapperFooter'
    
    export const NlyWrapperFooter = Vue.extend({
        name: name,
        props: {
            size: {
                type: String,
            },
        },
        computed: {
            footerFontSizeClass: function () {
                return this.size == 'sm'
                    ? 'text-sm'
                    : this.size == 'lg'
                    ? 'text-lg'
                    : ''
            },
        },
        render(h) {
            return h(
                'footer',
                {
                    staticClass: 'main-footer',
                    class: [this.footerFontSizeClass],
                },
                this.$slots.default
            )
        },
    })
    

    注册

    将以上组件注册,就可以在 vue 中直接使用了

    注册代码 demo

    import NlyWrapper from './wrapper.js'
    const WrapperPlugin = {
        install: function (Vue) {
            Vue.component('nly-wrapper', NlyWrapper)
        },
    }
    export { WrapperPlugin }
    

    效果

    代码。代码里使用了 height,是因为封装大的组件高度都是由子元素决定的。没有子元素就先用 height 撑起来看效果

    <template>
        <nly-wrapper layout="fixed">
            <nly-wrapper-header style="height:57px"> header</nly-wrapper-header>
            <nly-wrapper-sidebar> left </nly-wrapper-sidebar>
            <nly-wrapper-content style="height:calc(100vh - 116px)">
                main</nly-wrapper-content
            >
            <nly-wrapper-footer style="height:57px"> footer</nly-wrapper-footer>
        </nly-wrapper>
    </template>
    
    image.png

    一定要看完

    组件封装中引入的文件,请移步Github查看。不然组件是跑不起来的。
    还缺一个收起左侧导航栏的按钮,这个下一篇文章再讲

    相关文章

      网友评论

          本文标题:Vue 封装 Adminle3 左右上中下布局

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