关于uni-app:uniapp自定义弹框uapopup组件增强版

94次阅读

共计 6746 个字符,预计需要花费 17 分钟才能阅读完成。

组件介绍

UA-Popup 一款基于 uniapp 开发的轻量级多端自定义弹框组件。反对超过 20+ 参数配置、组件式 + 函数式两种调用形式。完满运行到 h5+App 端 + 小程序Nvue原生组件页面。

如上图:兼容 h5+ 小程序 +App 端,运行成果统一。

导入组件

在 main.js 中全局引入组件。

import UAPopup from './components/ua-popup/index.vue'
Vue.component('ua-popup', UAPopup)

当然,也反对 easycom 模式。能够省略下面这一步导入。

反对 组件式写法 函数调用 两种形式。

  • 组件式调用
<!-- msg 提醒 -->
<ua-popup v-model="showMsg" anim="fadeIn" content="上善若水, 水利万物而不争" shadeClose="false" time="3" />

<!-- 信息框 -->
<ua-popup v-model="showInfo" anim="scaleIn"
    content="阳光下人走不出本人的影子, 光明中人看不见本人的影子。只有还有今天, 明天就永远是起跑线。"
    :btns="[{text: '晓得了', style: 'color:#999;', click: hideInfo},
    ]"
/>

  • 函数式调用
// 函数式嵌套调用
handleInfo() {
    let $ua = this.$refs.uapopup
    let $toast = this.$refs.uatoast
    $ua.open({
        content: '人生漫漫,且行且珍惜',
        customStyle: {'background-color': 'rgba(170, 0, 127, 0.6)', 'color': '#fff'},
        time: 3,
        onClose() {
            $ua.open({
                type: 'android',
                content: '<div style="color:#aa007f"> 预测将来的最好方法是本人亲手发明将来 </div>',
                customStyle: {'width': '200px'},
                zIndex: 202120,
                btns: [
                    {text: 'close', click() {$ua.close()
                        }
                    },
                    {
                        text: 'Get 一下',
                        style: 'color:#00aa00;',
                        click() {
                            $toast.open({
                                type: 'toast',
                                icon: 'loading',
                                content: '请稍后...',
                                opacity: .2,
                                time: 2,
                                zIndex: 202125,
                            })
                        }
                    }
                ]
            })
        }
    })
},
handleBtnClick() {
    this.$refs.uapopup.open({
        content: '正在操作中,请稍后...',
        shadeClose: false,
        anim: 'footer',
        customStyle: {'background-color': 'rgba(0, 170, 0, 0.6)', 'color': '#fff', 'border-radius': '6px'},
        opacity: .1,
        time: 2,
        onClose: () => {
            this.$refs.uatoast.open({type: 'toast', icon: 'success', content: '操作胜利', time: 2,})
        }
    });
},

  • 底部面板弹框

<!-- 底部对话框 -->
<ua-popup v-model="showFooter" anim="footer" type="footer" :shadeClose="false"
    content="真正觉醒的一刻,是放下追寻外在世界的财产,而开始追寻内心世界的真正财产。"
    :btns="[{text: 'Get 到了', style: 'color:#00e0a1;', click: handleInfo},
        {text: '珍藏', style: 'color:#ee0a24;'},
        {text: '勾销', style: 'color:#a9a9a9;', click: hideFooter},
    ]"
/>

<!-- ActionSheet 底部弹出式菜单 -->
<ua-popup v-model="showActionPicker" anim="footer" type="actionsheetPicker" round title="题目"
    :btns="[{text: '勾销'},
        {text: '确定', style: 'color:#00aa00;', click: handleInfo},
    ]"
>
    <!-- 自定义内容 -->
    <ul class="list" style="padding:50px;">
        <li> 只有不失去方向,就不会失去自我 </li>
        <li> 别问他人为什么,多问本人凭什么 </li>
        <li> 不要期待机会,而要发明机会 </li>
    </ul>
</ua-popup>
  • Toast 轻提示框

<ua-popup v-model="showToast" type="toast" icon="loading" time="2" content="加载中..." />
  • 仿微信长按弹框成果

<!-- 长按弹窗 1 -->
<ua-popup v-model="showContextMenu1" type="contextmenu" :follow="follow1" opacity=".35"
    :btns="[{text: '置顶聊天', click: handleContextPopup},
        {text: '标记为未读', style: 'color:#00aa00;'},
        {text: '少一点预设的期盼,那份对人的关心会更自在', style: 'color:#ff007f;'},
        {text: '心有多大, 舞台就有多大', style: 'color:#09f;'},
        {text: '敞开', style: 'color:#aaaa7f;', click: hideContextMenu1},
    ]"
>
</ua-popup>

<!-- 长按弹窗 2 -->
<ua-popup v-model="showContextMenu2" type="contextmenu" :follow="follow2" opacity="0"
    :btns="[{text: '置顶联系人', click: handleContextPopup},
        {text: '设置备注信息'},
        {text: '星标好友'},
        {text: '删除', click: hideContextMenu1},
    ]"
>
</ua-popup>

开发实现

  • 自定义参数配置
props: {value: { type: Boolean, default: false},
    title: String,
    content: String,
    type: String,
    customStyle: {type: Object, default: null},
    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: '202107' },
    btns: {type: Array, default: null},
    // 关上弹框回调
    onOpen: {type: Function, default: null},
    // 敞开弹框回调
    onClose: {type: Function, default: null},
},
  • 弹框模板
<template>
    <!-- #ifdef APP-NVUE -->
    <view v-if="opts.visible" class="ua__popup" :class="{'ua__popup-closed': closeAnim}">
    <!-- #endif -->
    <!-- #ifndef APP-NVUE -->
    <view v-show="opts.visible" class="ua__popup" :class="{'ua__popup-closed': closeAnim}">
    <!-- #endif -->
        <!-- 遮罩层 -->
        <view v-if="opts.shade && opts.shade!='false'"class="uapopup__overlay"@touchstart="handleShadeClick":style="{'opacity': opts.opacity >= 0 ? opts.opacity : '','z-index': oIndex-1}"></view>
        <!-- 窗口层 -->
        <view class="uapopup__wrap" :style="{'z-index': oIndex}">
            <view class="uapopup__child" :id="'uapopup-'+uuid":class="['anim-'+opts.anim, opts.type&&'popui__'+opts.type, opts.round&&'round', opts.position]":style="[opts.follow&&positionStyle, opts.customStyle]">
                <!-- // 题目 -->
                <view v-if="opts.title || $slots.title" class="uapopup__title">
                    <template v-if="$slots.title"><slot name="title" /></template>
                    <rich-text v-else :nodes="opts.title"></rich-text>
                </view>
                
                <!-- //toast -->
                <!-- <view v-if="opts.type=='toast'&&opts.icon" class="toast__icons" :class="['toast__icons-'+opts.icon]" :style="{'background-image': `url(${toastIcon[opts.icon]})`}"></view> -->
                <image v-if="opts.type=='toast'&&opts.icon" class="toast__icons" :class="['toast__icons-'+opts.icon]" :src="toastIcon[opts.icon]" mode="widthFix"></image>
                <!-- // 内容 -->
                <view v-if="opts.content || $slots.content" class="uapopup__content">
                    <template v-if="$slots.content"><slot name="content" /></template>
                    <rich-text v-else :nodes="opts.content"></rich-text>
                </view>
                <slot />
                
                <!-- // 按钮组 -->
                <view v-if="opts.btns" class="uapopup__actions">
                    <rich-text v-for="(btn,index) in opts.btns" :key="index" class="btn" :class="{'disabled': btn.disabled}" :style="btn.style" @click="handleBtnClick($event, index)" :nodes="btn.text"></rich-text>
                </view>
                
                <!-- // 敞开按钮 -->
                <view v-if="opts.xclose" class="uapopup__xclose" :class="opts.xposition" :style="{'color': opts.xcolor}" @click="close"></view>
            </view>
        </view>
    </view>
</template>

/**
 * @Desc     uniapp 全端自定义弹框组件
 * @Time     andy by 2021/7/10
 * @About    Q:282310962  wx:xy190310
 */
<script>
    let index = 0
    export default {
        ...
        data() {
            return {
                // 混入 props 参数,处理函数式调用
                opts: {visible: false,},
                toastIcon: {...},
                closeAnim: false,
                oIndex: 202107,
                timer: null,
                // 长按定位初始化(防止弹框跳动闪动)positionStyle: {position: 'absolute', left: '-999px', top: '-999px'},
            }
        },
        watch: {value(val) {
                const type = val ? 'open' : 'close'
                this[type]()}
        },
        computed: {uuid() {return Math.floor(Math.random() * 10000)
            },
        },
        methods: {
            // 关上弹框
            open(options) {if(this.opts.visible) return
                this.opts = Object.assign({}, this.$props, options)
                this.opts.visible = true
                
                // nvue 的各组件在安卓端默认是通明的,如果不设置 background-color,可能会导致呈现重影的问题
                // #ifdef APP-NVUE
                if(!this.opts.customStyle['background'] && !this.opts.customStyle['background-color']) {this.opts.customStyle['background'] = '#fff'
                }
                // #endif
                
                let _index = ++index
                this.oIndex = _index + parseInt(this.opts.zIndex)
                
                this.$emit('open')
                typeof this.opts.onOpen === 'function' && this.opts.onOpen()
                
                // 长按解决
                if(this.opts.follow) {...}
                
                ...
            },
            // 敞开弹框
            close() {if(!this.opts.visible) return
                
                this.closeAnim = true
                setTimeout(() => {
                    this.opts.visible = false
                    this.closeAnim = false
                    
                    this.$emit('input', false)
                    this.$emit('close')
                    typeof this.opts.onClose === 'function' && this.opts.onClose()
                    
                    this.timer && clearTimeout(this.timer)
                    delete this.timer
                }, 200)
            },
            
            ...
            
            // 获取 dom 宽高
            getDom(id) {return new Promise((resolve, inject) => {uni.createSelectorQuery().in(this).select('#uapopup-' + id).fields({size: true,}, data => {resolve(data)
                    }).exec()})
            },
            
            // 自适应坐标点
            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>

在 nvue 页面运行成果。仍然能够笼罩在 video 原生组件之上。

大家能够依据我的项目须要,本人定制一些扩大性能。实现多样化弹框场景。

ok,应用 uniapp 自定义弹框组件就介绍到这里。心愿各位喜爱哈~~✍

uniapp 自定义导航条 + 底部 tabbar 组件

正文完
 0