1

Component introduction

UA-Popup a lightweight multi-terminal custom popup component developed based on uniapp Support more than 20+ parameter configuration, component type + function type two calling methods. It runs perfectly to h5+App terminal+small programs and Nvue native component page.

As shown above: Compatible with h5+small program+App terminal, the operation effect is the same.

Import components

Introduce components globally in main.js.

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

Of course, easycom mode is also supported. You can omit the import step above.

Support component type writing and function call two ways.

  • Component call
<!-- 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},
    ]"
/>

  • Functional call
// 函数式嵌套调用
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,
            })
        }
    });
},

  • Bottom panel bullet frame

<!-- 底部对话框 -->
<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 light prompt box

<ua-popup v-model="showToast" type="toast" icon="loading" time="2" content="加载中..." />
  • Imitate the effect of long press on WeChat

<!-- 长按弹窗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>

Development and realization

  • Custom parameter configuration
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 },
},
  • Bullet frame template
<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>

Run the effect on the nvue page. It can still be overlaid on the video native components.

You can customize some extended functions according to the needs of the project. Achieve diversified bullet frame scenes.

Ok, that's it for using uniapp to customize the bullet frame component. I hope you like it~~✍

uniapp custom navigation bar + bottom tabbar component


xiaoyan2017
765 声望318 粉丝

web前端开发爱好者,专注于前端h5、jquery、vue、react、angular等技术研发实战项目案例。