The origin of

During this period of time, I have been developing the Nuxt.js project, and many places in the project need to use the shell layer function. I originally wanted to directly use the Vant component library instead of repeating the wheel. Behind due to various considerations, decided to develop a new one, so there is a Vue custom popup window VPopup.

VPopup is a mobile popup component built on vue. js. It integrates the functions of Popup layer, Notify message, Dialog Dialog box, ActionSheet action panel box and Toast weak prompt box in _Vant_ and _NutUI_.

Using the component

Import components globally in main.js

import Popup from './components/popup' 

Vue.use(Popup)

Vpopup supports both label and function calls.

<! <template> <view id="root">... <! --> <v-popup v-model="showDialog" anim="scaleIn" title=" title "content=" popup" --> <v-popup v-model="showDialog" anim="scaleIn" title=" title "content=" popup" shadeClose="false" xclose :btns="[ {...}, {...}, ]" /> </view> </template> <! <script> export default {... Methods: {handleShowDialog() {let $el = this.$vpopup({title: 'title ', content:' popup '); ', anim: 'scaleIn', shadeClose: false, xclose: true, onOpen: () => {console.log('vpopup is opened! ')}, BTNS: [{text: 'cancelled'}, {text: 'sure, style, color: # 00 a0f1, click: () = > {$el. The close ()}}]}); } } } </script>Copy the code

You can choose an appropriate call method according to actual use requirements.

The following describes several common pop-up scenarios

  • MSG information box

<! "ShadeClose ="false" time="3" /> <v-popup v-model="showMsg" anim="fadeIn" content=" shadeClose="false" time="3" /> <v-popup v-model="showMsgBg" anim="footer" Content =" Custom background color "Shade ="false" time="3" Popup - style = "background: rgba (0, 0,. 6); color:#fff;" / > <! --> <v-popup v-model="showConfirm" shadeClose="false" title=" warning "xclose z-index="2001" content="<div style='color:#00e0a1; padding:20px 40px; </div>" : BTNS ="[{text: 'cancel ', click: () => showConfirm=false}, {text:' confirm ', style: 'color:#e63d23;', click: handleInfo}, ]" />Copy the code
  • Wechat /Android effects popup window

<! <v-popup v-model="showAndroid1" type="android" shadeClose="false" xclose title=" title "z-index="2002" BTNS ="[{text: 'know ', click: () => showAndroid1=false}, {text:' know ', style: 'color:#00e0a1;', click: handleInfo}, ]" > </v-popup>Copy the code
  • Toast weak prompt box

<! <v-popup V-model ="showToast" type=" Toast" icon="loading" time="2" content=" loading..." /> < V-popup V-model ="showToast" type="toast" icon="success" shade="false" time="2" Content ="success hint "/> < V-popup V-model ="showToast" type="toast" icon="fail" Shade ="false" time="2" Content =" failure prompt "/>Copy the code
  • ActionSheet Action dialog box

<! <v-popup v-model="showFooter" anim="footer" type="footer" :shadeClose="false" z-index="1001" Content =" Are you sure you want to delete this data? Data can be recovered within 7 days after deletion. After 7 days, data cannot be recovered." : BTNS ="[{text: 'restore ', style: 'color:# 00e0A1;', click: handleInfo}, {text:' delete ', style: 'color:#ee0a24;', click:" () = > null}, {text: 'cancel', style: 'color: # a9a9a9;', click: () = > showFooter = false},] "/ > <! ActionSheet bottom popup menu --> < V-popup V-model ="showActionPicker" anim="footer" type="actionsheetPicker" round title=" Title content" : BTNS ="[{text: 'cancel ', click: () => showActionPicker=false}, {text:' confirm ', style: 'color:# 00e0A1;', click:" () => null}, ]" > <! > < span style=" color: RGB (51, 51, 51); font-family: arial, sans-serif; text-align:center;" > < li > backpack < / li > < li > shoes < / li > < li > sweatpants < / li > < / ul > < / v - popup >Copy the code

Okey, I’m not going to do a code demo here. Continue to look at the implementation process

implementation

Default Parameter Settings

@ @ Props -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - v model whether the current component displays the title title content content (support custom slots) type pop-up window type (toast | Footer | actionsheet | actionsheetPicker | android/ios) popupStyle custom pop-up window style icon toast icon (loading | success | fail) shade Whether display mask layer shadeClose whether to click on the mask when close the pop-up window opacity mask layer transparency whether round display whether rounded corners xclose close icon xposition close icon position (left) | top | | right bottom Xcolor close icon color anim popup animation (scaleIn | fadeIn | footer | fadeInUp | fadeInDown) position pop-up position (top | | | right bottom left) ZIndex popup stack (default 8080) BTNS popup button (parameter: Text | style | disabled | click) @ @ $emit -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - open to open the popup layer is triggered when the close (@ open = "XXX") Triggered when closing the pop-up layer (@ close = "XXX") @ @ Event -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- onOpen open the popup window callback onClose close the popup window callbackCopy the code

The above parameters can be used in both label and function expressions.

Popup. Vue template

<template> <div v-show="opened" class="nuxt__popup" :class="{'nuxt__popup-closed': closeCls}" :id="id"> <div v-if="JSON.parse(shade)" class="nuxt__overlay" @click="shadeClicked" :style="{opacity}"></div>  <div class="nuxt__wrap"> <div class="nuxt__wrap-section"> <div class="nuxt__wrap-child" :class="['anim-'+anim, type&&'popui__'+type, round&&'round', position]" :style="popupStyle"> <div v-if="title" class="nuxt__wrap-tit" v-html="title"></div> <div v-if="type=='toast'&&icon" class="nuxt__toast-icon" :class="['nuxt__toast-'+icon]" v-html="toastIcon[icon]"></div> <template v-if="$slots.content"><div class="nuxt__wrap-cnt"><slot name="content" /></div></template> <template v-else><div v-if="content" class="nuxt__wrap-cnt" v-html="content"></div></template> <slot /> <div v-if="btns" class="nuxt__wrap-btns"> <span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" v-html="btn.text"></span> </div> <span v-if="xclose" class="nuxt__xclose" :class="xposition" :style="{'color': Xcolor}" @click="close"></span> </div> </div> </div> </div> </template> /** * @desc VueJs custom popup component VPopup * @time Andy by $index = 0, $lockCount = 0, $timer = {}; export default { props: { ... }, data() { return { opened: false, closeCls: '', toastIcon: { ... } } }, watch: { value(val) { const type = val ? 'open' : 'close'; this[type](); },}, methods: {// Open pop-ups open() {if(this.opened) return; this.opened = true; this.$emit('open'); typeof this.onOpen === 'function' && this.onOpen(); if(JSON.parse(this.shade)) { if(! $lockCount) { document.body.classList.add('nt-overflow-hidden'); } $lockCount++; If (this.time) {$index++; if($timer[$index] ! == null) clearTimeout($timer[$index]) $timer[$index] = setTimeout(() => { this.close(); }, parseInt(this.time) * 1000); } if(this.follow) { this.$nextTick(() => { let obj = this.$el.querySelector('.nuxt__wrap-child'); let oW, oH, winW, winH, pos; oW = obj.clientWidth; oH = obj.clientHeight; winW = window.innerWidth; winH = window.innerHeight; pos = this.getPos(this.follow[0], this.follow[1], oW, oH, winW, winH); obj.style.left = pos[0] + 'px'; obj.style.top = pos[1] + 'px'; }); }}, // Close popover close() {if(! this.opened) return; this.closeCls = true; setTimeout(() => { this.opened = false; this.closeCls = false; if(JSON.parse(this.shade)) { $lockCount--; if(! $lockCount) { document.body.classList.remove('nt-overflow-hidden'); } } if(this.time) { $index--; } this.$emit('input', false); this.$emit('close'); typeof this.onClose === 'function' && this.onClose(); }, 200); }, 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) } }, getZIndex() { for(var $idx = parseInt(this.zIndex), $el = document.getElementsByTagName('*'), i = 0, len = $el.length; i < len; i++) $idx = Math.max($idx, $el[i].style.zIndex) return $idx; }, getPos(x, y, ow, oh, winW, winH) {let l = (x + ow) > winW? x - ow : x; let t = (y + oh) > winH ? y - oh : y; return [l, t]; } }, } </script>Copy the code

The component calls the open and close methods using the watch method to listen for incoming V-Model values.

watch: { value(val) { const type = val ? 'open' : 'close'; this[type](); }},Copy the code

Right-click or long – press the popover to customize slot contents.

<! <v-popup v-model="showContextMenu2" type="contextmenu" :follow="followPos" opacity="0" : BTNS ="[{text: 'Marked as unread ', click: handleContextPopup}, {text:' MESSAGE do not disturb '}, {text: 'top chat '}, {text:' delete ', click: () => showContextMenu2=false}, ]" />Copy the code

Functional call implementation

The component implements functional invocation via vue.extend instance constructor.

import Vue from 'vue'; import VuePopup from './popup.vue'; let PopupConstructor = Vue.extend(VuePopup); let $instance; Let VPopup = function (options = {}) {/ / the same page, id the same Popup DOM will only have a options. The id = options. The id | | 'nuxt - the Popup - id; $instance = new PopupConstructor({ propsData: options }); $instance.vm = $instance.$mount(); let popupDom = document.querySelector('#' + options.id); if(options.id && popupDom) { popupDom.parentNode.replaceChild($instance.$el, popupDom); } else { document.body.appendChild($instance.$el); } Vue.nextTick(() => { $instance.value = true; }) return $instance; } VPopup.install = () => { Vue.prototype['$vpopup'] = VPopup; Vue.component('v-popup', VuePopup); } export default VPopup;Copy the code

Mount the $VPOPup method to the Vue prototype and register the V-POPup component.

This.$vpopup({… }) function to make the call.

$vpopup({this.$vpopup({... })) */ let $el = this.$vpopup({title: 'title ', content:' <div style="padding:20px; > <p> Mount via Vue, call popovers functionally. </p> <p style="color:#999;" >this.$vpopup({... })</p> </div> ', BTNS: [{text: 'cancel ', click: () = > {$el / / close the Windows. The close (); / / dynamic updating contents / / $el. The content = ` < p style = "color: red; padding: 20 px;" > here is the dynamic updates! < / p > `;}}, {text: 'confirm ', style: 'color:#09f;', click: () => {this.$vpopup({id: 'nuxt-popup ', // set id type: 'toast', icon: Opacity:.2, time: 2,}}},], onOpen() {console.log('is opend! ')}, onClose: () => {console.log('is closed! ')}})},}Copy the code

Okay, based on the Vue. Js | Nuxt. Js custom pop-up layer components is introduced here. VPopup is currently being used in the nuxt.js project, and will be shared at that time. I hope it will be helpful to you! 💪 ✍

Attached is a recent Flutter instance project

Flutter + Dart imitation WeChat chat App interface instance | Flutter chat rooms