vue3.x自定義彈框元件|vue3.0移動端彈窗|vue3全域性元件

xiaoyan2015發表於2021-01-06

之前有開發過一個vue2手機端彈層,今天分享的是Vue3版彈框元件。
V3Popup 基於vue3.0開發的簡易手機版彈框元件。支援6+彈框型別、7+動畫效果、20+自定義引數配置。


在功能及效果上和之前vue2基本保持一致。

通過如下方法即可快速引入v3popup彈框元件。

import { createApp } from 'vue'
import App from './App.vue'

// 引入彈窗元件v3popup
import V3Popup from './components/v3popup'

createApp(App).use(V3Popup).mount('#app')

支援如下20+自定義引數任意搭配,實現各種彈窗效果。

|props引數|
v-model         是否顯示彈框
title           標題
content         內容(支援String、帶標籤內容、自定義插槽內容)***如果content內容比較複雜,推薦使用標籤式寫法
type            彈窗型別(toast | footer | actionsheet | actionsheetPicker | android | ios)
popupStyle      自定義彈窗樣式
icon            toast圖示(loading | success | fail)
shade           是否顯示遮罩層
shadeClose      是否點選遮罩時關閉彈窗
opacity         遮罩層透明度
round           是否顯示圓角
xclose          是否顯示關閉圖示
xposition       關閉圖示位置(left | right | top | bottom)
xcolor          關閉圖示顏色
anim            彈窗動畫(scaleIn | fadeIn | footer | fadeInUp | fadeInDown)
position        彈出位置(top | right | bottom | left)
follow          長按/右鍵彈窗(座標點)
time            彈窗自動關閉秒數(123)
zIndex          彈窗層疊(預設8080)
teleport        指定掛載節點(預設是掛載元件標籤位置,可通過teleport自定義掛載位置) teleport="body | #xxx | .xxx"
btns            彈窗按鈕(引數:text|style|disabled|click)
++++++++++++++++++++++++++++++++++++++++++++++
|emit事件觸發|
success         層彈出後回撥(@success="xxx")
end             層銷燬後回撥(@end="xxx"++++++++++++++++++++++++++++++++++++++++++++++
|event事件|
onSuccess       層開啟回撥事件
onEnd           層關閉回撥事件

v3popup同樣的支援函式式+元件式兩種方式。

  • 函式式
    let $el = this.$v3popup({
      title: '標題',
      content: '<div style='color:#f90;padding:10px;'>這裡是內容資訊!</div>',
      type: 'android',
      shadeClose: false,
      xclose: true,
      btns: [
          {text: '取消', click: () => { $el.close(); }},
          {text: '確認', style: 'color:#09f;', click: () => handleOK},
      ],
      onSuccess: () => {},
      onEnd: () => {}
    })
  • 元件式
    <v3-popup v-model="showMsg" anim="fadeIn" content="msg提示框測試(2s後視窗關閉)" shadeClose="false" time="2" />
    <v3-popup v-model="showConfirm" shadeClose="false" title="標題" xclose z-index="2021"
      content="<div style='color:#ff5252;padding:20px;'>確認框(這裡是確認框提示資訊,這裡確認框提示資訊,這裡是確認框提示資訊)</div>"
      :btns="[
          {text: '取消', click: () => showConfirm=false},
          {text: '確定', style: 'color:#09f;', click: handleInfo},
      ]"
    />

v3popup元件模板及核心邏輯處理部分。

<template>
    <div ref="elRef" v-show="opened" class="vui__popup" :class="{'vui__popup-closed': closeCls}" :id="id">
        <!-- //蒙層 -->
        <div v-if="JSON.parse(shade)" class="vui__overlay" @click="shadeClicked" :style="{opacity}"></div>
        <div class="vui__wrap">
            <div class="vui__wrap-section">
                <div class="vui__wrap-child" :class="['anim-'+anim, type&&'popupui__'+type, round&&'round', position]" :style="[popupStyle]">
                    <div v-if="title" class="vui__wrap-tit" v-html="title"></div>
                    <div v-if="type=='toast'&&icon" class="vui__toast-icon" :class="['vui__toast-'+icon]" v-html="toastIcon[icon]"></div>
                    <!-- 判斷插槽是否存在 -->
                    <template v-if="$slots.content">
                        <div class="vui__wrap-cnt"><slot name="content" /></div>
                    </template>
                    <template v-else>
                        <div v-if="content" class="vui__wrap-cnt" v-html="content"></div>
                    </template>
                    <slot />
                    <div v-if="btns" class="vui__wrap-btns">
                        <span v-for="(btn, index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event, index)" v-html="btn.text"></span>
                    </div>
                    <span v-if="xclose" class="vui__xclose" :class="xposition" :style="{'color': xcolor}" @click="close"></span>
                </div>
            </div>
        </div>
    </div>
</template>

/**
 * @Desc     Vue3.0自定義彈層V3Popup
 * @Time     andy by 2020-12
 * @About    Q:282310962  wx:xy190310
 */
<script>
    import { onMounted, ref, reactive, watch, toRefs, nextTick } from 'vue'
    let $index = 0, $locknum = 0, $timer = {}
    export default {
        props: {
            // 接收父元件v-model值,如果v-model:open,則這裡需寫open: {...}
            modelValue: { type: Boolean, default: false },
            // 識別符號,相同ID共享一個例項
            id: {
                type: String, default: ''
            },
            title: String,
            content: String,
            type: String,
            popupStyle: String,
            icon: String,
            shade: { type: [Boolean, String], default: true },
            shadeClose: { type: [Boolean, String], default: true },
            opacity: { type: [Number, String], default: '' },
            round: Boolean,
            xclose: Boolean,
            xposition: { type: String, default: 'right' },
            xcolor: { type: String, default: '#333' },
            anim: { type: String, default: 'scaleIn' },
            position: String,
            follow: { type: Array, default: null },
            time: { type: [Number, String], default: 0 },
            zIndex: { type: [Number, String], default: '8080' },
            teleport: [String, Object],
            btns: {
                type: Array, default: null
            },
            onSuccess: { type: Function, default: null },
            onEnd: { type: Function, default: null },
        },
        emits: [
            'update:modelValue'
        ],
        setup(props, context) {
            const elRef = ref(null)

            const data = reactive({
                opened: false,
                closeCls: '',
                toastIcon: {
                    ...
                }
            })

            onMounted(() => {
                ...
            })

            // 監聽彈層v-model
            watch(() => props.modelValue, (val) => {
                if(val) {
                    open()
                }else {
                    close()
                }
            })

            // 開啟彈層
            const open = () => {
                if(data.opened) return
                data.opened = true
                typeof props.onSuccess === 'function' && props.onSuccess()

                const dom = elRef.value
                dom.style.zIndex = getZIndex() + 1

                ...

                // 倒數計時
                if(props.time) {
                    $index++
                    // 避免重複操作
                    if($timer[$index] !== null) clearTimeout($timer[$index])
                    $timer[$index] = setTimeout(() => {
                        close()
                    }, parseInt(props.time) * 1000)
                }

                // 長按|右鍵選單
                if(props.follow) {
                    ...
                }
            }

            // 關閉彈層
            const close = () => {
                if(!data.opened) return

                data.closeCls = true
                setTimeout(() => {
                    ...

                    context.emit('update:modelValue', false)
                    typeof props.onEnd === 'function' && props.onEnd()
                }, 200)
            }

            // 點選遮罩層
            const shadeClicked = () => {
                if(JSON.parse(props.shadeClose)) {
                    close()
                }
            }
            // 按鈕事件
            const btnClicked = (e, index) => {
                let btn = props.btns[index];
                if(!btn.disabled) {
                    typeof btn.click === 'function' && btn.click(e)
                }
            }

            ...

            return {
                ...toRefs(data),
                elRef,
                close,
                shadeClicked,
                btnClicked,
            }
        }
    }
</script>

大家也可以根據以上模板或邏輯自行開發出一些想要的功能。

支援類似有讚的圓角彈框效果,可自定義彈出位置及關閉按鈕位置。

vue3.0中支援createAppcreateVNode render兩種方式來擴充套件例項函式,將彈框元件掛載到body上。

let $inst
// 建立掛載例項
let createMount = (opts) => {
    const mountNode = document.createElement('div')
    document.body.appendChild(mountNode)

    const app = createApp(PopupConstructor, {
        ...opts, modelValue: true,
        remove() {
            app.unmount(mountNode)
            document.body.removeChild(mountNode)
        }
    })
    return app.mount(mountNode)
}

好了,基於vue3開發移動端彈框就分享這麼多。感興趣的可以自己去動手試一試哈。?

electron+vue 仿微信客戶端聊天|electron 仿微信介面|electron 聊天例項

本作品採用《CC 協議》,轉載必須註明作者和本文連結
本文為原創文章,未經作者允許不得轉載,歡迎大家一起交流 QQ(282310962) wx(xy190310)

相關文章