最近用vue封裝了一個adminlte3組件庫,已經封裝了差不多25個組件了扣墩。很多封裝adminlte的都是引入jq。扛吞。這個我有點無語呻惕。沒有歧視的意思,用jq還用什么vue滥比。而且vue中用jq會出現(xiàn)很多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的頁面結構
這個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: {
//邊側欄最小化
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這個指令就用來控制左側收起展開的讥蟆。在綁定這個指令的元素上點擊就會觸發(fā)對應事件。
指令組件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);
};
// 隊列執(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設置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();
}
};
這時候組件跟指令都寫好了,注冊組件跟指令笛钝,然后把對應的代碼替換掉质况,就能實現(xiàn)左側收起展開了。
效果圖