功能描述:应业务需求及代码规范,对el-drawer抽屉组件进行全局封装,不需页面引用子组件可唤起抽屉页面并允许动态传参。
- 功能使用需先按需引入element ui组件
- element ui官网地址:https://element.eleme.cn/#/zh-CN/component/installation
- 拓展toLowerCase:把所有字符转为小写
- 拓展toUpperCase:把字符串转换为大写
1. 封装js代码并在main.js中引入
// 可直接全部复制并在utils文件夹中生成drawer.js文件
import Vue from 'vue'
import store from '@/store'
function snake2Camel(str, capLower) {
let s = str.replace(/[-_](\w)/g, function (x) {
return x.slice(1).toUpperCase();
});
s = s.replace(/^\w/, function (x) {
return capLower ? x.toLowerCase() : x.toUpperCase();
});
return s;
}
/**
* [camel2Snake 驼峰转蛇形]
* @param {String} str [description]
* @return {String} [description]
*/
function camel2Snake(str) {
return str.replace(/([A-Z])/g, "-$1").toLowerCase();
}
let mix = {
methods: {
async payload(fn, fail) {
try {
this.fullscreenLoading = true;
await fn();
} catch (e) {
console.error(e);
}
this.fullscreenLoading = false;
}
}
};
// 获取需要动态创建的抽屉组件
const drawersContext = require.context("../", true, /\$([a-zA-Z\-0-9]+)\.vue$/);
const drawers = drawersContext.keys().reduce((views, key) => {
const fileName = key.match(/\$([a-zA-Z\-0-9]+)\.vue$/i)[1];
if (!fileName) return views;
let componentName = camel2Snake(fileName);
let clsName = snake2Camel(componentName);
return Object.assign(views, {
[clsName]: drawersContext(key)
});
}, {});
// 已创建的抽屉实例
window.drawerExamples = {}
/**
* [createDrwaer 创建抽屉组件]
* @param {[type]} component [组件]
* @param {[type]} data [参数]
* @return {[type]} [description]
*/
const createDrwaer = function (temp, data, callback, name) {
let opt = {
data,
callback
};
let component = Object.assign({}, temp);
let initData = {
visible: true,
fullscreenLoading: false,
drawerID: name, //此组件唯一id
drawerBckend: false, //是否已加入后台
};
Object.assign(initData, component.data());
opt.data && Object.assign(initData, JSON.parse(JSON.stringify(opt.data)));
component.data = function () {
return initData;
}
// 创建构造器创建实例挂载
let DrawerC = Vue.extend({
...component,
store
});
let drawer = new DrawerC();
// 关闭事件
let _onClose = drawer.$options.methods.onClose;
drawer.$watch("visible", function (n, o) {
drawer === false && drawer.onClose();
});
drawer.onClose = function () {
drawer.drawerBckend = false
drawer.$destroy();
_onClose && _onClose.call(drawer);
if (document.body.contains(drawer.$el)) {
document.body.removeChild(drawer.$el);
};
if (drawer.drawerID && window.drawerExamples[drawer.drawerID]) {
delete window.drawerExamples[drawer.drawerID]
}
};
// 回调事件
let _onCallback = drawer.$options.methods.onCallback;
drawer.onCallback = function (...arg) {
try {
_onCallback && _onCallback();
if (callback && typeof callback === 'function') {
callback(...arg, drawer);
}
if (drawer.drawerID && window.drawerExamples[drawer.drawerID]) {
delete window.drawerExamples[drawer.drawerID]
}
} catch (e) {
// console.log(e);
}
};
drawer.$mount();
drawer.$watch("visible", function (n, o) {
drawer.onClose() && drawer === false;
});
drawer.$store = store
window.drawerExamples[name] = drawer
if (drawer.drawerID) {
Object.keys(window.drawerExamples).forEach((name) => {
window.drawerExamples[name].style.zIndex = '1999'
})
drawer.$el.style.zIndex = '2000'
}
drawer.$el.drawer.$el.onmousedown = () => {
if (drawer.drawerID) {
Object.keys(window.drawerExamples).forEach((name) => {
window.drawerExamples[name].$el.style.zIndex = '1999'
})
window.drawerExamples[drawer.drawerID].$el.style.zIndex = '2000'
}
}
document.body.appendChild(drawer.$el);
return drawer
};
/**
* [createDrwaerAsync promise创建抽屉]
* @param {[type]} temp [description]
* @param {[type]} data [description]
* @param {[Function]} callback [description]
* @return {[type]} [description]
*/
function createDrwaerAsync(temp, data, name) {
return new Promise(function (resolve, reject) {
// 初始化配置参数
let opt = {
data
};
let compnent = Object.assign({}, temp);
let initData = {
visible: true,
fullscreenLoading: false,
drawerID: name, //此组件唯一id
drawerBckend: false, //是否已加入后台
};
Object.assign(initData, component.data());
opt.data && Object.assign(initData, JSON.parse(JSON.stringify(opt.data)));
component.data = function () {
return initData;
}
// 创建构造器创建实例挂载
let DrawerC = Vue.extend({
...component,
store
});
let drawer = new DrawerC();
// 关闭事件
let _onClose = drawer.$options.methods.onClose;
drawer.onClose = function () {
try {
drawer.drawerBckend = false
resolve();
drawer.$destroy();
_onClose && _onClose.call(drawer);
if (document.body.contains(drawer.$el)) {
document.body.removeChild(drawer.$el);
};
if (drawer.drawerID && window.drawerExamples[drawer.drawerID]) {
delete window.drawerExamples[drawer.drawerID]
}
} catch (e) {
// console.log(e);
}
};
// 回调事件
let _onCallback = drawer.$options.methods.onCallback;
drawer.onCallback = function (...arg) {
try {
_onCallback && _onCallback();
resolve(...arg);
drawer.$destroy();
_onClose && _onClose.call(drawer);
if (callback && typeof callback === 'function') {
callback(...arg, drawer);
}
if (drawer.drawerID && window.drawerExamples[drawer.drawerID]) {
delete window.drawerExamples[drawer.drawerID]
}
} catch (e) {
// console.log(e);
}
};
drawer.$watch("visible", function (n, o) {
drawer.onClose() && drawer === false;
});
drawer.$mount();
drawer.$store = store
window.drawerExamples[name] = drawer
drawer.$el.onmousedown = () => {
if (drawer.drawerID && window.drawerExamples[drawer.drawerID]) {
Object.keys(window.drawerExamples).forEach((name) => {
window.drawerExamples[name].$el.style.zIndex = '1999'
})
window.drawerExamples[drawer.drawerID].$el.style.zIndex = '2000'
}
}
setTimeout(() => {
if (drawer.drawerID) {
Object.keys(window.drawerExamples).forEach((name) => {
window.drawerExamples[name].$el.style.zIndex = '1999'
})
drawer.$el.style.zIndex = '2002'
}
}, 10);
document.body.appendChild(drawer.$el);
});
}
/**
* [init 初始化]
* @param {[type]} components [description]
* @return {[type]} [description]
*/
function init(values) {
let drawerComponents = {};
if (!values) return;
Object.keys(values).forEach((name) => {
let ComponentContext = values[name].default;
ComponentContext.mixins = [mix];
drawerComponents[name] = function (data, callback) {
if (callback && typeof callback === 'function') {
return createDrwaer.call(this, ComponentContext, data, callback, name);
}
// 如果已存在此组件那么关闭
if (window.drawerExamples[name] && !window.drawerExamples[name].drawerBckend) {
window.drawerExamples[name].onClose()
delete window.drawerExamples[name]
// 如果已经存在此组件并且该组件在后台中那么return
} else if (window.drawerExamples[name] && window.drawerExamples[name].drawerBckend) {
return ''
}
return createDrwaerAsync.call(this, ComponentContext, data, name);
};
});
return drawerComponents;
}
// 清空所有抽屉
export function cleardrawerExamples() {
if (!window.drawerExamples) return
Object.keys(window.drawerExamples).forEach(name => {
window.drawerExamples[name] && window.drawerExamples[name].onClose()
delete window.drawerExamples[name]
})
}
// 获取所有抽屉实例
export function getAlldrawerExamples() {
return window.drawerExamples || {}
}
// 删除指定抽屉
export function delDrawerId(drawerID) {
if (window.drawerExamples && window.drawerExamples[drawerID] && !window.drawerExamples[drawerID].drawerBckend) {
window.drawerExamples[drawerID].onClose()
}
}
// 将指定抽屉加入后台
export function addDrawerBckEnd(drawerID) {
if (window.drawerExamples && window.drawerExamples[drawerID] && !window.drawerExamples[drawerID].drawerBckend) {
window.drawerExamples[drawerID].drawerBckend = true
window.drawerExamples[drawerID].$el.querySelector(".el-drawer").style.top = '-100%'
}
}
// 全局挂载
Vue.prototype.$drawer = init(drawers);
Vue.prototype.$delDrawerId = delDrawerId
Vue.prototype.$addDrawerBckEnd = addDrawerBckEnd
// main.js引用挂载在vue中
import '@/utils/drawer.js'
2. 生成全局弹框组件页面(例文件名:$drawer.vue)
- 例生成文件路径:src/component/$drawer.vue
- 示例主要使用全局的$drawer方法,关闭指定弹框可根据上面js自行修改
// 当前组件的名称需要加上前缀$符号,对应js中的挂载对象,visible为js中的监听对象,不需要在data中定义
// el-drawer上绑定的参数为其他页面调用组件时传来的动态参数
<template>
<el-drawer
:title="title"
:visible.sync="visible"
:direction="direction"
:wrapperClosable="false"
:modal="false"
>
<span>页面内容展示</span>
<span @click="handCls">关闭按钮</span>
<span @click="handCallback">关闭按钮回调</span>
</el-drawer>
</template>
mounted() {
//在页面初始化的时候可获取从调用页面传递过来的自定义数据
console.log(this.row, this.title)
},
// methods方法
methods: {
// 关闭事件可直接调用onClose方法,但是onClose不支持传值,只会关闭弹框页面
handCls() {
this.onClose()
},
// onCallback方法支持回调,但是唤起弹框的方法需要改为异步接收回调参数,onCallback调用后也可关闭弹框,不需要再次调用onClose方法
handCallback() {
this.onCallback(true, '传值回调')
}
}
3. 组件调用
- 在A页面调用公共组件的$drawer.vue文件
// html
<template>
<div @click="handClickBtn">按钮调用组件</div>
</template>
// methods方法
methods: {
// this.$drawer可直接拿到,如果是在嵌套方法内可修改this指向,
// Drawer为新增的组件文件名称,首字母需大写
handClickBtn() {
this.$drawer.Drawer({
title: '传值title', // 默认绑定值
row: [], //可自定义传参对象,在弹框页面获取时用this.row即可
})
}
// 也可将handClickBtn方法前加上async变成异步状态,接受弹框onCallback回调事件
async handClickBtn() {
let result = this.$drawer.Drawer({
title: '传值title', // 默认绑定值
row: [], //可自定义传参对象,在弹框页面获取时用this.row即可
})
// result为接收的onCallback回调的参数
if (result) {
// ...TODO
}
}
}
el-dialog同理也可使用此方法进行二次封装,只需将js中获取的class名以及挂载对象替换就可以,页面使用el-dialog组件