Vue 封装 Adminle3 左右上中下布局

2020-04-18  本文已影响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 类主要在左右布局中使用。

组件

分解一下组件。

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

<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查看。不然组件是跑不起来的。
还缺一个收起左侧导航栏的按钮,这个下一篇文章再讲

上一篇下一篇

猜你喜欢

热点阅读