【uniapp-小程序】实现方法调用的全局tips弹窗

发布于:2025-02-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

笑死,只有在想找工作的时候才会想更新博客。

开发背景

本来是个uniapp开发的H5项目,但是由于上面要求需要转为小程序版本,导致很多原先支持的功能都需要针对小程序环境进行兼容。这次先说说自定义的全局提示弹窗。代码中使用了较多uniapp组件,详细使用方法请查看uniapp官方文档
框架:uniapp、小程序、vue2

弹窗组件

弹窗组件其实就是个常规的vue页面,通过设置蒙层+层级实现弹窗效果,实现简单,可扩展性强。

  • 样式中使用了import语法是为了覆盖uniapp原生样式,如不需要可去除;
  • safeAreaTop为自定义字段,为获取的小程序顶部状态栏高度;
  • 弹窗内容显示使用了uniapp的rich-text组件,如对弹窗内容显示要求没那么高的可以直接展示文本,如:<text decode>{{content}}</text>,其中decode是为了实现换行;
  • 很多配置项是为了满足我项目的需求,并非必须,可按需调整。
<template>
	<view class="confirm-modal" v-if="visible" :style="{top:`${safeAreaTop}px`}">
		<view class="confirm-modal-mask flex justify-center align-center">
			<view class="confirm-modal-content" :style="contentStyle">
				<view class="close-top-btn flex justify-end align-center" v-if="closeEnabled">
					<uni-icons type="closeempty" size=20 color="#999" @click="handleClose"></uni-icons>
				</view>
				<view class="title flex justify-center align-center" v-if="title">
					{{title}}
				</view>
				<view :style="{marginTop: closeEnabled&&!title?'24px':0}"
			class="flex justify-center align-center text-center">
					<rich-text :nodes="content"></rich-text>
				</view>
				<view class="flex justify-between align-center operation">
					<button type="primary" :plain="cancelClassName.indexOf('cancel')>-1" v-if="showCancel" @click="handleClose" class="round" :class="cancelClassName"> 
						{{cancelText}} 
					</button>
					<button type="primary" :plain="confirmClassName.indexOf('cancel')>-1" v-if="showConfirm" @click="handleOk" class="round" :class="confirmClassName">
						{{confirmText}} 
					</button>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		mapGetters
	} from 'vuex'
	export default {
		data() {
			return {
				safeAreaTop: this.$safeAreaTop, // 获取小程序顶部状态栏高度,按需使用
			}
		},
		computed: {
			...mapGetters(['confirmModal']),
			title() {
				return this.confirmModal.title
			},
			content() {
				return this.confirmModal.content
			},
			// 默认:default, 整行: block
			btnType() {
				return this.confirmModal.btnType || 'default'
			},
			cancelText() {
				return this.confirmModal.cancelText || '取消'
			},
			confirmText() {
				return this.confirmModal.confirmText || '确定'
			},
			cancelClass() {
				return this.confirmModal.cancelClass
			},
			confirmClass() {
				return this.confirmModal.confirmClass
			},
			showCancel() {
				return typeof this.confirmModal.showCancel === 'boolean' ? this.confirmModal.showCancel : true
			},
			showConfirm() {
				return typeof this.confirmModal.showConfirm === 'boolean' ? this.confirmModal.showConfirm : true
			},
			visible() {
				return typeof this.confirmModal.visible === 'boolean' ? this.confirmModal.visible : false
			},
			success() {
				return this.confirmModal.success
			},
			cancel() {
				return this.confirmModal.cancel
			},
			closeEnabled() {
				return typeof this.confirmModal.closeEnabled === 'boolean' ? this.confirmModal.closeEnabled : false
			},
			contentStyle() {
				return this.confirmModal.contentStyle
			},
			cancelClassName() {
				if (this.cancelClass !== '') {
					return this.cancelClass
				}
				if (this.btnType === 'block') {
					return 'cancel-btn-block'
				}
				return 'cancel-btn'
			},
			confirmClassName() {
				if (this.confirmClass !== '') {
					return this.confirmClass
				}
				if (this.btnType === 'block') {
					return 'confirm-btn-block'
				}
				return 'confirm-btn'
			}
		},
		beforeDestroy() {
			if (this.visible) {
				this.$store.commit('SET_CONFIG', {
					visible: false
				})
			}
		},
		methods: {
			handleClose() {
				if (typeof this.cancel === 'function') {
					this.cancel()
				}
				this.$store.commit('SET_CONFIG', {
					visible: false
				})

			},
			handleOk() {
				if (typeof this.success === 'function') {
					this.success()
				}
				this.$store.commit('SET_CONFIG', {
					visible: false
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
	.confirm-modal {
		height: 100%;
		width: 100%;
		overflow: hidden;
		z-index: 12;
		position: absolute;
		top: 0;
		left: 0;

		.confirm-modal-mask {
			background-color: rgba(0, 0, 0, 0.6);
			width: 100%;
			height: 100%;

			.confirm-modal-content {
				background-color: #fff;
				border-radius: 6px;
				width: 85%;
				padding: 24px;
				position: relative;

				.title {
					font-weight: bold;
					margin: 8px 0 12px 0;
					font-size: 32rpx;
				}

				.close-top-btn {
					position: absolute;
					right: 12px;
					top: 12px;
				}

				.operation {
					width: 100%;
					background-color: #fff;
					flex-wrap: wrap;

					.round {
						margin-top: 24px;
					}
					.cancel-btn{
						border-color:#00AEB8 !important;
				        color:#00AEB8  !important;
						width: 45%;
						font-size: 30rpx;
						white-space: nowrap;
					 }
					 .confirm-btn{
						background-color: #00AEB8  !important;
						width: 45%;
						font-size: 30rpx;
					    white-space: nowrap;
					 }
					 .cancel-btn-block{
						border-color:#00AEB8 !important;
					    color:#00AEB8  !important;
						width: 100%;
						font-size: 30rpx;
					    white-space: nowrap;
					 }
					 .confirm-btn-block{
					  	background-color: #00AEB8  !important;
						width: 100%;
					    font-size: 30rpx;
						white-space: nowrap;
					 }
				}
			}
		}
	}
</style>

弹窗效果如下所示:
在这里插入图片描述

全局调用

当我们写好弹窗组件时,就可以在需要的页面内引入调用(以上代码中各配置项的输入需更改为props传入,或通过ref绑定组件,调用组件内方法传入)。但是每次使用都得引入、调用,过于繁琐。为了方便使用,可以封装为全局引入,并在需要的页面内通过方法调用,实现方案如下:

封装配置项入参

在store内定义一个modules用来存储弹窗配置项并在getters内声明(按需):

const confirmModal = {
	state: {
		config: {
			title: '',
			content: '',
			btnType: 'default',
			cancelText: '取消',
			confirmText: '确定',
			cancelClass: '',
			confirmClass: '',
			showCancel: true,
			showConfirm: true,
			visible: false,
			success: null,
			cancel: null,
			closeEnabled: false,
			contentStyle: '',
		}
	},
	mutations: {
		SET_CONFIG: (state, config) => {
			const defaultConfig = {
				content: '',
				btnType: 'default',
				cancelText: '取消',
				confirmText: '确定',
				cancelClass: '',
				confirmClass: '',
				showCancel: true,
				showConfirm: true,
				visible: true,
				success: null,
				cancel: null,
				title: '',
				closeEnabled: false,
				contentStyle: ''
			}
			if (typeof config === 'string') {
				state.config = Object.assign(defaultConfig, {
					content: options
				});
			} else if (typeof config === 'object') {
				state.config = Object.assign(defaultConfig, config);
			}
		}
	}
}
export default confirmModal

组件内的参数从store获取,想要显示弹窗时调用this.$store.commit('SET_CONFIG', {}),第二个参数为弹窗配置内容。

全局注入

在main.js文件内注入组件:

// 引入组件,请根据自己定义组件的实际路径和名称引入
import ConfirmModal from './components/confirm-modal/confirm-modal.vue' 
Vue.component('confirm-modal', ConfirmModal)
// 每次都通过$store调用过于繁琐,所以定义了一个全局方法
Vue.prototype.$confirmModal = (config) => {
	store.commit('SET_CONFIG', config)
}
// 获取小程序顶部状态栏高度
const {
	safeArea
} = wx.getWindowInfo()
Vue.prototype.$safeAreaTop = safeArea && safeArea.top || 0

使用

this.$confirmModal({
	content: '请确认是否删除该条跟发规则?',
	cancelText: '取消',
	confirmText: '确认',
	success: () => {}
})

调用后,效果如上图

附带:如何在uniapp-H5项目中实现全局自定义弹窗

组件定义

组件的基本内容不变,主要是将弹窗配置项作为props转入,如下:
在这里插入图片描述

定义vue插件

在组件目录下定义index.js文件:

import Vue from 'vue'
import confirmModalVue from './confirm-modal.vue';
// 定义插件对象
const confirmModal = {};
// vue的install方法,用于定义vue插件
confirmModal.install = function(Vue, options) {
	const confirmModalInstance = Vue.extend(confirmModalVue);
	let currentMsg;
	const initInstance = () => {
		// 实例化vue实例
		currentMsg = new confirmModalInstance();
		let msgBoxEl = currentMsg.$mount().$el;
		document.body.appendChild(msgBoxEl);
	};
	// 在Vue的原型上添加实例方法,以全局调用
	Vue.prototype.$confirmModal = (options) => {
		if (!currentMsg) {
			initInstance();
		}
		const config = {
			content: '',
			btnType: 'default',
			cancelText: '取消',
			confirmText: '确定',
			cancelClass: '',
			confirmClass: '',
			showCancel: true,
			showConfirm: true,
			visible: true,
			success: null,
			cancel: null,
			title: '',
			closeEnabled: false,
			contentStyle: ''
		}
		if (typeof options === 'string') {
			Object.assign(currentMsg, config, {
				content: options
			});
		} else if (typeof options === 'object') {
			Object.assign(currentMsg, config, options);
		}
		return currentMsg; // 为了链式调用
	};
};
export default confirmModal;

引入

在main.js内引入:

import ConfirmModal from './components/confirm-modal'
Vue.use(ConfirmModal)