為了響應式研究了一下 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 全是 jsx 封裝的組件,大部分是函數(shù)式組件,渲染速度是要比.vue 組件快。
這個組件庫沒有引入 jq。注意沒有引入 JQ。很多 Adminlte Vue 的組件庫都是引入了 JQ,這樣肯定是不行的,多少會有 bug,至于為什么不行,請理解 Vue 的精髓, 數(shù)據(jù)驅動 目前差不多快 50 個復用組件了。
adminlte3 的布局有一種 collapse 布局,也就是左邊側導航欄,右側上中下布局,且邊側導航欄可以收起展開。
admintle3 控制布局的 class 主要在<body>標簽上。左右布局中,主要有 3 個 css 類來控制布局。這三個 css 類主要在左右布局中使用。
第一個是 layout-fixed,這個主要是控制右側上中下布局在初始情況下有一個 margin-left: 250px 的屬性,即偏移左側 250px,騰出空間給左側。且還有一個作用是讓左側導航欄滾動條與窗口滾動條不沖突。
第二個是 sidebar-mini,這個允許左側導航欄在設置好的斷點狀態(tài)能收起到邊側只顯示圖標。如果沒有這個,邊側導航欄收起會直接消失,不出現(xiàn)在窗口中。
第三個是 sidebar-collapse。實際上應該還有一個 sidebar-open。不過 sidebar-open 是在小屏情況下,展開左側導航欄才會出現(xiàn)。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 如下
| 參數(shù) | 類型 | 默認值 | 描述 |
|---|---|---|---|
| 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 如下
| 參數(shù) | 類型 | 默認值 | 描述 |
|---|---|---|---|
| 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 如下
| 參數(shù) | 類型 | 默認值 | 描述 |
|---|---|---|---|
| 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 如下
| 參數(shù) | 類型 | 默認值 | 描述 |
|---|---|---|---|
| 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 如下
| 參數(shù) | 類型 | 默認值 | 描述 |
|---|---|---|---|
| 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>

一定要看完
組件封裝中引入的文件,請移步Github查看。不然組件是跑不起來的。
還缺一個收起左側導航欄的按鈕,這個下一篇文章再講