vue.js构建的轻量级PC网页端交互式弹层组件VLayer。

前段时间有分享过一个vue移动端弹窗组件,今天给大家分享一个最近开发的vue pc端弹出层组件。

VLayer 一款集Alert、Dialog、Message、Notification、ActionSheet、Toast、Popover、Popconfirm等众多功能于一身的轻量级pc弹层组件。通过精巧的逻辑、布局解决复杂的对话框功能,为你呈现多样化弹出框效果!

VLayer 在设计开发之初借鉴了业界有名的Layer弹窗、Element-UI、iView等组件化设计思想。

  • 提供了2种调用方式(标签式 | 函数式)
  • 12+弹窗类型(toast、footer、actionsheet、actionsheetPicker、android/ios、contextmenu、drawer、iframe、message、notify、popover)
  • 内置7种弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)

◆ 引入组件

在main.js中引入vlayer组件。

import VLayer from './components/vlayer'

Vue.use(VLayer)

vlayer支持标签式函数式两种调用方式。

  • 标签式写法
<v-layer
v-model="showConfirm"
title="标题内容"
content="<div style='color:#00e0a1;padding:20px;'>这里是内容信息!</div>"
xclose
z-index="2001"
lockScroll="false"
resize
dragOut
:btns="[
{text: '关闭', click: () => showConfirm=false},
{text: '确认', style: 'color:#f90;', click: handleXXX},
]"
/>
  • 函数式写法

vlayer同样支持函数式写法  this.$vlayer({...})  支持30+中参数灵活配置。

注意:当使用类型为message | notify | popover弹窗时候,调用方式只能是函数式 this.$vlayer.message({...})  this.$vlayer.notify({...})  this.$vlayer.popover({...})

let $vlayero = this.$vlayer({
title: '标题内容',
content: '<div style='color:#00e0a1;padding:20px;'>这里是内容信息!</div>',
xclose: true,
zIndex: 2001,
lockScroll: false,
resize: true,
dragOut: true,
btns: [
{text: '关闭', click: () => { $vlayero.close(); }},
{text: '确认', click: () => this.handleXXX()},
]
});

◆ 一览效果

◆ 实现过程

  • vlayer默认配置参数
@@Props
v-model 当前组件是否显示
title 标题
content 内容(支持自定义插槽内容)
type 弹窗类型(toast | footer | actionsheet | android/ios | contextmenu | drawer | iframe | message/notify/popover)
layerStyle 自定义弹窗样式
icon toast图标(loading | success | fail)
shade 是否显示遮罩层
shadeClose 是否点击遮罩时关闭弹窗
lockScroll 是否弹窗出现时将 body 滚动锁定
opacity 遮罩层透明度
xclose 是否显示关闭图标
xposition 关闭图标位置(left | right | top | bottom)
xcolor 关闭图标颜色
anim 弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)
position 弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)
drawer 抽屉弹窗(top | right | bottom | left)
follow 跟随元素定位弹窗(支持元素.kk #kk 或 [e.clientX, e.clientY])
time 弹窗自动关闭秒数(1、2、3)
zIndex 弹窗层叠(默认8080)
topmost 置顶当前窗口(默认false)
area 弹窗宽高(默认auto)设置宽度area: '300px' 设置高度area:['', '200px'] 设置宽高area:['350px', '150px']
maxWidth 弹窗最大宽度(只有当area:'auto'时,maxWidth的设定才有效)
maximize 是否显示最大化按钮(默认false)
fullscreen 全屏弹窗(默认false)
fixed 弹窗是否固定
drag 拖拽元素(可定义选择器drag:'.xxx' | 禁止拖拽drag:false)
dragOut 是否允许拖拽到窗口外(默认false)
resize 是否允许拉伸尺寸(默认false)
btns 弹窗按钮(参数:text|style|disabled|click)
------------------------------------------
@@$emit
open 打开弹出层时触发(@open="xxx")
close 关闭弹出层时触发(@close="xxx")
------------------------------------------
@@Event
onOpen 打开弹窗回调
onClose 关闭弹窗回调
  • vlayer弹窗模板
<template>
<div v-show="opened" class="vui__layer" :class="{'vui__layer-closed': closeCls}" :id="vlayerId">
<!-- //遮罩 -->
<div v-if="JSON.parse(shade)" class="vlayer__overlay" @click="shadeClicked" :style="{opacity}"></div>
<!-- //窗体 -->
<div class="vlayer__wrap" :class="['anim-'+anim, type&&'popui__'+type, drawer&&'popui__drawer-'+drawer, xclose&&'vlayer-closable', tipArrow]" :style="layerStyle">
<div v-if="title" class="vlayer__wrap-tit" v-html="title"></div>
<div v-if="type=='toast'&&icon" class="vlayer__toast-icon" :class="['vlayer__toast-'+icon]" v-html="toastIcon[icon]"></div>
<div class="vlayer__wrap-cntbox">
<template v-if="$slots.content">
<div class="vlayer__wrap-cnt"><slot name="content" /></div>
</template>
<template v-else>
<template v-if="content">
<iframe v-if="type=='iframe'" scrolling="auto" allowtransparency="true" frameborder="0" :src="content"></iframe>
<!-- message|notify|popover -->
<div v-else-if="type=='message' || type=='notify' || type=='popover'" class="vlayer__wrap-cnt">
<i v-if="icon" class="vlayer-msg__icon" :class="icon" v-html="messageIcon[icon]"></i>
<div class="vlayer-msg__group"><div v-if="title" class="vlayer-msg__title" v-html="title"></div><div class="vlayer-msg__content" v-html="content"></div></div>
</div><div v-else class="vlayer__wrap-cnt" v-html="content"></div>
</template>
</template>
<slot />
</div>
<div v-if="btns" class="vlayer__wrap-btns">
<span v-for="(btn,index) in btns" :key="index" class="btn" :class="{'btn-disabled': btn.disabled}" :style="btn.style" @click="btnClicked($event,index)" v-html="btn.text"></span>
</div>
<span v-if="xclose" class="vlayer__xclose" :class="!maximize&&xposition" :style="{'color': xcolor}" @click="close"></span>
<span v-if="maximize" class="vlayer__maximize" @click="maximizeClicked($event)"></span>
<span v-if="resize" class="vlayer__resize"></span>
</div>
<!-- 修复拖拽卡顿 -->
<div class="vlayer__dragfix"></div>
</div>
</template>
/**
* @Desc vue.js自定义pc端弹窗组件VLayer
* @Time andy by 2020-10-28
* @About Q:282310962 wx:xy190310
*/
<script>
import domUtils from './utils/dom'
let $index = 0, $lockCount = 0, $timer = {};
let ie = !!window.ActiveXObject || "ActiveXObject" in window;
export default {
props: {
// ...
},
data() {
return {
opened: false,
closeCls: '',
toastIcon: {
// ...
},
messageIcon: {
// ...
},
vlayerOpts: {},
tipArrow: null,
}
},
mounted() {
window.addEventListener('resize', () => {
this.autopos();
})
},
computed: {
vlayerId() {
return this.id ? this.id : `vlayer-${domUtils.generateId()}`;
}
},
watch: {
value(val) {
const type = val ? 'open' : 'close';
this[type]();
},
},
methods: {
// 打开弹窗
open() {
if(this.opened) return;
this.opened = true;
this.$emit('open');
typeof this.onOpen === 'function' && this.onOpen(); const dom = this.$el;
this.$nextTick(() => {
document.body.appendChild(dom); this.auto();
}) this.callback();
},
// 关闭弹窗
close() {
if(!this.opened) return; let dom = this.$el;
let vlayero = dom.querySelector('.vlayer__wrap');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let omax = dom.querySelector('.vlayer__maximize'); this.closeCls = true;
setTimeout(() => {
this.opened = false;
this.closeCls = false;
if(this.vlayerOpts.lockScroll) {
$lockCount--;
if(!$lockCount) {
document.body.style.paddingRight = '';
document.body.classList.remove('nt-overflow-hidden');
}
}
if(this.time) {
$index--;
}
// 清除弹窗样式
vlayero.style.width = vlayero.style.height = vlayero.style.top = vlayero.style.left = '';
ocnt.style.height = '';
omax && omax.classList.contains('maximized') && omax.classList.remove('maximized');
this.vlayerOpts.isBodyOverflow && (document.body.style.overflow = ''); this.$emit('input', false);
this.$emit('close');
typeof this.onClose === 'function' && this.onClose();
}, 200);
}, // 弹窗位置
auto() {
// ... this.autopos(); // 全屏弹窗
if(this.fullscreen) {
this.full();
} // 弹窗拖动|缩放
this.move();
}, autopos() {
if(!this.opened) return;
let oL, oT;
let pos = this.position;
let isFixed = JSON.parse(this.fixed);
let dom = this.$el;
let vlayero = dom.querySelector('.vlayer__wrap'); let area = [domUtils.client('width'), domUtils.client('height'), vlayero.offsetWidth, vlayero.offsetHeight] oL = (area[0] - area[2]) / 2;
oT = (area[1] - area[3]) / 2; if(this.follow) {
this.offset();
}else {
typeof pos === 'object' ? (
oL = parseFloat(pos[0]) || 0, oT = parseFloat(pos[1]) || 0
) : (
// ...
) vlayero.style.left = parseFloat(isFixed ? oL : domUtils.scroll('left') + oL) + 'px';
vlayero.style.top = parseFloat(isFixed ? oT : domUtils.scroll('top') + oT) + 'px';
}
}, // 元素跟随定位
offset() {
let oW, oH, pS;
let dom = this.$el;
let vlayero = dom.querySelector('.vlayer__wrap'); oW = vlayero.offsetWidth;
oH = vlayero.offsetHeight;
pS = domUtils.getFollowRect(this.follow, oW, oH);
this.tipArrow = pS[2]; vlayero.style.left = pS[0] + 'px';
vlayero.style.top = pS[1] + 'px';
}, // 最大化弹窗
full() {
let timer;
let isFixed = JSON.parse(this.fixed);
let dom = this.$el;
let vlayero = dom.querySelector('.vlayer__wrap');
let otit = dom.querySelector('.vlayer__wrap-tit');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let obtn = dom.querySelector('.vlayer__wrap-btns');
let omax = dom.querySelector('.vlayer__maximize'); let t = otit ? otit.offsetHeight : 0;
let b = obtn ? obtn.offsetHeight : 0; let rect = [
parseFloat(domUtils.getStyle(vlayero, 'left')), parseFloat(domUtils.getStyle(vlayero, 'top')),
parseFloat(domUtils.getStyle(vlayero, 'width')), parseFloat(domUtils.getStyle(vlayero, 'height'))||vlayero.offsetHeight
]
this.vlayerOpts.rect = rect; clearTimeout(timer);
timer = setTimeout(() => {
vlayero.style.left = isFixed ? 0 : domUtils.scroll('left') + 'px';
vlayero.style.top = isFixed ? 0 : domUtils.scroll('top') + 'px';
vlayero.style.width = domUtils.client('width') + 'px';
vlayero.style.height = domUtils.client('height') + 'px';
ocnt.style.height = domUtils.client('height') - t - b + 'px';
}, 16);
}, // 恢复弹窗
restore() {
let dom = this.$el;
let vlayero = dom.querySelector('.vlayer__wrap');
let otit = dom.querySelector('.vlayer__wrap-tit');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let obtn = dom.querySelector('.vlayer__wrap-btns');
let omax = dom.querySelector('.vlayer__maximize'); let t = otit ? otit.offsetHeight : 0;
let b = obtn ? obtn.offsetHeight : 0; vlayero.style.left = parseFloat(this.vlayerOpts.rect[0]) + 'px';
vlayero.style.top = parseFloat(this.vlayerOpts.rect[1]) + 'px';
vlayero.style.width = parseFloat(this.vlayerOpts.rect[2]) + 'px';
vlayero.style.height = parseFloat(this.vlayerOpts.rect[3]) + 'px';
ocnt.style.height = parseFloat(this.vlayerOpts.rect[3]) - t - b + 'px';
}, // 拖动|缩放弹窗
move() {
// ...
}, // 事件处理
callback() {
// 倒计时关闭
if(this.time) {
$index++;
// 防止重复点击
if($timer[$index] !== null) clearTimeout($timer[$index])
$timer[$index] = setTimeout(() => {
this.close();
}, parseInt(this.time) * 1000);
}
},
// 点击最大化按钮
maximizeClicked(e) {
let o = e.target;
if(o.classList.contains('maximized')) {
// 恢复
this.restore();
} else {
// 最大化
this.full();
}
},
// 点击遮罩层
shadeClicked() {
if(JSON.parse(this.shadeClose)) {
this.close();
}
},
// 按钮事件
btnClicked(e, index) {
let btn = this.btns[index];
if(!btn.disabled) {
typeof btn.click === 'function' && btn.click(e)
}
},
},
}
</script>

dom.js中是一些常用的函数。当弹窗出现,滚动条隐藏,避免出现页面移位。

getScrollBarSize: function() {
if (scrollBarWidth !== undefined) return scrollBarWidth; const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer); const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll'; const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner); const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll; return scrollBarWidth;
},

vlayer弹窗通过 Vue.extend 扩展函数实现函数式调用。

import Vue from 'vue';
import VueLayer from './vlayer.vue'; let VLayerConstructor = Vue.extend(VueLayer); let $instance;
let _getInstance = props => {
return new VLayerConstructor(props)
} let VLayer = function(options = {}) {
// ...
} /**
* Message 消息提示
* @param {Object} options
*/
VLayer.message = function(options) {
typeof options == 'string' && (options = { content: options });
return VLayer({
icon: 'info', anim: 'fadeInUp', position: 't', lockScroll: false, shade: false, time: 2, layerStyle: 'margin-top:15px;',
...options,
title: null, type: 'message'
})
} /**
* Notification 通知提醒
* @param {Object} options
*/
VLayer.notify = function(options) {
typeof options == 'string' && (options = { content: options });
return VLayer({
anim: 'fadeInRight', position: 'rt', lockScroll: false, shade: false, xclose: true, time: 4.5, layerStyle: 'margin:20px 0 0 -20px;',
...options,
type: 'notify'
})
} /**
* Popover 弹出框 | Popconfirm 气泡确认框
* @param {Object} options
*/
VLayer.popover = function(options) {
return VLayer({
anim: 'fadeIn', lockScroll: false, shade: false, xclose: true,
...options,
type: 'popover'
})
} VLayer.install = () => {
Vue.prototype['$vlayer'] = VLayer;
Vue.component('v-layer', VueLayer);
} export default VLayer;

默认标题区可以拖拽,也可以自定义拖拽抓手。设置 drag: '#xxx',或者 drag: false 禁止拖拽。

<!-- 拖拽弹窗 -->
<v-layer
v-model="showDragable"
title="拖放弹窗"
xclose
maximize
:area="['', '580px']"
drag="#dragGuesture"
dragOut
>
<div slot="content">
<p id="dragGuesture" style="padding:30px;">
<span style="background:#ff0;color:#00e0a1;">我是自定义拖拽抓手 drag:'#dragGuesture' <br>拖动我试一试!<br>
<em style="color:red;">设置drag:false可禁止拖拽!</em><br>
<em style="color:red;">设置dragOut:true可拖拽到窗口外!</em>
</span>
</p>
<img @click="handleInfo" src="xxx.jpg" style="max-width:100%;" />
</div>
</v-layer>
  • message 信息框

<!-- 普通提示 -->
this.$vlayer.message('这是一条提示信息'); <!-- 成功提示 -->
this.$vlayer.message({content: '这是一条成功的提示', icon: 'success'}); <!-- 错误提示(自定义背景) -->
this.$vlayer.message({content: ' 这是一条错误的提示', icon: 'error', layerStyle: 'background:#ffefe6;color:#ff3838;margin-top:15px;'});
  • notification 通知框

<!-- 成功通知 -->
this.$vlayer.notify({title: '成功', content: '这是一条成功的提示', icon: 'success'});
  • popover / popconfirm 气泡框

<!-- 使用方法和上面差不多,只不过需要加上follow定位元素 -->
let $fw = this.$vlayer.popover({
follow: '#popover4',
icon: 'warning',
content: '这是一段内容确定删除吗?',
time: null,
xclose: false,
btns: [
{text: 'no', click: () => { $fw.close(); }},
{text: 'yes', click: () => this.handleXXX()},
],
onClose: function() {
this.$vlayer.message({content: 'success closed', icon: 'success'})
}
});

另外还支持iframe弹窗类型,设置 type: 'iframe',content传入网址即可。

<!-- iframe弹窗 -->
<v-layer
title="iframe弹窗"
content="https://cn.vuejs.org/"
v-model="showIframe"
type="iframe"
lock-scroll="false"
xclose
maximize
resize
:area="['915px', '595px']"
/>

okay,以上就是基于vue.js实现pc端弹窗的一些分享,希望对大家有所帮助!

最后附上vue移动端弹窗及nuxt实例项目

vue.js移动端弹框:https://www.cnblogs.com/xiaoyan2017/p/13776977.html

nuxt.js+vue聊天实例:https://www.cnblogs.com/xiaoyan2017/p/13823195.html

基于Vue.js PC桌面端弹出框组件|vue自定义弹层组件|vue模态框的更多相关文章

  1. vue3系列:vue3.0自定义全局弹层V3Layer|vue3.x pc桌面端弹窗组件

    基于Vue3.0开发PC桌面端自定义对话框组件V3Layer. 前两天有分享一个vue3.0移动端弹出层组件,今天分享的是最新开发的vue3.0版pc端弹窗组件. V3Layer 一款使用vue3.0 ...

  2. 基于VUE.JS的移动端框架Mint UI

    Mint UI GitHub:github.com/ElemeFE/mint 项目主页:mint-ui.github.io/# Demo:elemefe.github.io/mint- 文档:mint ...

  3. Vue 爬坑之路(十一)—— 基于 Nuxt.js 实现服务端渲染(SSR)

    直接使用 Vue 构建前端单页面应用,页面源码时只有简单的几行 html,这并不利于网站的 SEO,这时候就需要服务端渲染 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了一个 ...

  4. Fenix – 基于 Node.js 的桌面静态 Web 服务器

    Fenix 是一个提供给开发人员使用的简单的桌面静态 Web 服务器,基于 Node.js 开发.您可以同时在上面运行任意数量的项目,特别适合前端开发人员使用. 您可以通过免费的 Node.js 控制 ...

  5. vue.js之生命周期,防止闪烁,计算属性的使用,vue实例简单方法和循环重复数据

    摘要:今天是比较糟糕的一天没怎么学习,原因是学校的wifi连不上了~~.今天学习一下vue的生命周期,如何防止闪烁(也就是用户看得到花括号),计算属性的使用,vue实例简单方法,以及当有重复数据时如何 ...

  6. Vue.js+vue-element搭建属于自己的后台管理模板:更深入了解Vue.js(三)

    前言 上一章我们介绍了关于Vue实例中一些基本用法,但是组件.自定义指令.Render函数这些放到了本章来介绍,原因是它们要比前面讲的要难一些,组件是Vue.js最核心的功能,学习使用组件也是必不可少 ...

  7. vue.js国际化vue-i18n插件的使用问题,在模版文本、组件方法、jsf方法里的使用

    vue.js国际化vue-i18n插件的使用问题,在模版文本.组件方法.jsf方法里的使用 1.在文本里使用{{$t("xxx")}} <span>{{$t(" ...

  8. 经验总结:WebBrowser自动点击弹出提示框alert、弹出对话框confirm、屏蔽弹出框、屏蔽弹出脚本错误的解决办法

    经验总结:WebBrowser自动点击弹出提示框alert.弹出对话框confirm.屏蔽弹出框.屏蔽弹出脚本错误的解决办法 网上有好多解决方法,可是不一定好使,本人经过多次试验,针对WebBrows ...

  9. Vue.js 源码分析(二十四) 高级应用 自定义指令详解

    除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令. 官网介绍的比较抽象,显得很高大上,我个人对自定义指令的理解是:当自定义指令作用在一些DOM元素或组件上 ...

随机推荐

  1. oracle数据库外部连接无法访问

    服务器出现的问题是运行的项目无法访问oracle数据库连接,用plsql输入用户名密码后卡死,无法连接.但是通过命令窗口对oracle数据库操作正常,对oracle服务进行查看并重启,并无异常,运行t ...

  2. 设计模式PDF下载了4.0万本!那,再肝一本《Java面经手册》吧!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 1. 先祝贺下自己拿下4.0万本下载量! <重学Java设计模式>PDF ...

  3. Visual Studio中Debug与Release以及x86、x64、Any CPU的区别 &&&& VS中Debug与Release、_WIN32与_WIN64的区别

    本以为这些无关紧要的 Debug与Release以及x86.x64.Any CPU 差点搞死人了. 看了以下博文才后怕,难怪我切换了一下模式,程序就pass了.... 转载: 1.https://ww ...

  4. C# 生成chart图表的三种方式

    .net中,微软给我们提供了画图类(system.drawing.imaging),在该类中画图的基本功能都有.比如:直线.折线.矩形.多边形.椭圆形.扇形.曲线等等,因此一般的图形都可以直接通过代码 ...

  5. shell-变量的数值运算let内置命令

    1. let命令的用法 格式: let 赋值表达式 [注]let赋值表达式功能等同于:((赋值表达式))  范例1:给自变量i加8 [root@1-241 scripts]# i=2 [root@1- ...

  6. python简单实现论文查重(软工第一次项目作业)

    前言 软件工程 https://edu.cnblogs.com/campus/gdgy/informationsecurity1812 作业要求 https://edu.cnblogs.com/cam ...

  7. 多测师讲解第一个月 _综合面试题_高级讲师肖sir

    第一个月综合面试题 1.  冒烟测试是什么意思?  对主要的用例测试 2.你们公司的项目流程是什么? 3.你们公司的bug分几个级别?  4个 4.你对外键是怎么理解的? 你会使用外键吗?给一个表添加 ...

  8. day45 Pyhton 数据库Mysql 02

    一.前期回顾 数据库 mysql的安装 配置环境 为什么要用数据库? 稳定性 一致性 并发 存取数据效率高 数据库的分类 关系型数据库 mysql oracle sqlserver 非关系型数据库 r ...

  9. golang的http库使用代理

    1.先上代码 package main import ( "crypto/tls" "flag" "fmt" "io/ioutil ...

  10. 【C语言程序设计】—最近超火的小游戏—【数字炸弹】!

    ✍  准备工作和建议 一.程序的原理 在动手编程之前,得先跟大家说一下这个程序是干什么的. 我们可以称呼这个游戏为<数字炸弹>. 游戏的原理是这样: 每一轮电脑从 1 到 100 中随机抽 ...