跨平台应用开发进阶(七) :uni-app 自定义 showToast

x33g5p2x  于2022-04-06 转载在 其他  
字(7.2k)|赞(0)|评价(0)|浏览(1086)

一、前言

利用uni-app跨平台开发框架开发多终端APP时,应用HBuilder自身提供的弹窗不满足业务需求,故开发自定义弹窗组件showToast。该组件主要实现以下功能:

  • 支持标题、内容、按钮自定义;
  • 支持根据按钮标识执行不同业务逻辑;
  • 支持内容自适应;
  • 支持弹窗关闭按钮自定义显示隐藏;

完整代码下载详参【拓展阅读】章节。

二、实现原理

应用自定义指令结合自定义组件实现视图渲染及控制逻辑,应用Vuex实现状态管理。

首先在main.js定义全局组件,并在initToast.js中注册$showToastVue原型上,以方便全局调用。

// main.js
import initToast from "@/components/bocft-toast/initToast.js"
import showToast from "@/components/bocft-toast/bocft-toast.vue"

initToast(Vue);
Vue.component('show-toast',showToast);

三、代码实现

initToast.js中注册$showToastVue原型上实现逻辑如下:

// initToast.js
import Vuex from 'vuex'
export default function initToast(v) {
  // 挂在store到全局Vue原型上
  v.prototype.$toastStore = new Vuex.Store({
    state: {
		show:false,
		icon:"success",//success:成功;fail:失败
		title:"标题",
		content:'内容',
		success:null,
		downLoadUrl:null,
		toastCloseIconShow: '',
		toastCloseIconStyle: '',
		......
    },
    mutations: {
		hideToast(state) {
			// 小程序导航条页面控制
			// #ifndef H5
			if(state.hideTabBar){
				wx.showTabBar();
			}
			// #endif
			state.show = false;
		},
		// 按钮1方法
		buttonOneClick(state, data) {
			let _this = this;
			console.log('data.flag:', data.flag)
			switch (data.flag) {
				case 'hide':
					state.show = false;
					break;
				......
			}
		},
		// 按钮2方法
		buttonTwoClick(state, data) {
			let _this = this;
			this.progress = 0;
			this.upshow = true; //切换进度条显示
			switch (data.flag) {
				case 'reload':
					state.show = false;
					break;
			}
		},
		close() {
			this.show2 = false; //关闭更新弹窗
			this.upshow = false; //关闭进度条
			uni.showTabBar(); //显示tabbar
			this.$emit('closeVersion');//关闭show组件
		},
		showToast(state,data) {
			state = Object.assign(state,data)
			console.log('state:', state);
			state.show = true
			// 按钮不展示时,延时2S隐藏提示框
			if (!state.buttonShow) {
				setTimeout(()=>{
					state.show = false
					return state.success(state.icon)
				},2000)	
			}
		},
		success(state,data) {
			// state = Object.assign(state,data)
			// console.log(state);
			// state.show = true
			// setTimeout(()=>{
			// 	state.show = false
			// 	return state.success(state.icon)
			// },2000)
		}
    }
  })
  // 注册$showToast到Vue原型上,以方便全局调用
  v.prototype.$showToast = function (option) { 
	if (typeof option === 'object') {
		// #ifndef H5
		if(option.hideTabBar){
			wx.hideTabBar();
		}
		// #endif
		console.log('-------------option------------', option)
		v.prototype.$toastStore.commit('showToast', option)
	}else{
		throw "配置项必须为对象传入的值为:"+typeof option;
	}
  }
}

视图渲染逻辑如下:

// toast.vue
<template>
	<view :class="buttonShow ? 'showToast' : '_showToast'" class="prompt-box" v-show="show" @touchmove.stop.prevent="moveHandle">
		<!-- 弹窗关闭按钮 -->
		<view v-if="toastCloseIconShow" @tap='closeToast'>
			<image class="Toast-close-icon" :style="{top: toastCloseIconStyle.top}" src='@/static/guanbi.png'></image>	
		</view>
		<view class="_shade">
		</view>
		<view class="_ToastBox">
			<view class="Toast-box" :style="{minHeight: toastBoxStyle.minHeight, borderRadius: toastBoxStyle.borderRadius, 
					top: toastBoxStyle.top, background: toastBoxStyle.background}">
				<view style="height: 10px;"></view>
				<!-- 成功图片 -->
				<image v-if="icon=='success' && successImgSrc != ''" :style="{width: successImgClass.width, height: successImgClass.height }" 
					class="Toast-icon" :src='successImgSrc'></image>
				<!-- 成功标题 -->
				<text v-if="icon=='success'" class="Toast-title-success" :style="{fontSize: toastTitleSuccessStyle.fontSize, 
					fontWeight: toastTitleSuccessStyle.fontWeight, top: toastTitleSuccessStyle.top }">{{title}}</text>
				<!-- 失败图片 -->
				<image v-if="icon=='fail'" class="Toast-icon" :src='failImgSrc' :style="{width: failImgClass.width, height: failImgClass.height }"></image>
				<!-- 失败标题 -->
				<text v-if="icon=='fail'" class="Toast-title-fail" :style="{top: toastTitleFailStyle.top}">{{title}}</text>
				<!-- 提示内容 -->
				<text class="Toast-subtitle" :style="{top: toastTitleSuccessStyle.toastSubtitleTop, color: toastTitleSuccessStyle.contentFontColor,
						fontSize: toastTitleSuccessStyle.contentFontSize}">{{content}}</text>
				<!-- 按钮组 -->
				<view v-if="buttonShow" class="buttonShowClass" :style="{top: buttonShowStyle.top}">
					<!-- 按钮1 -->
					<view v-if="buttonOneShow" class="sendBtn"
						:style="{fontFamily: buttonShowStyle.fontFamily, fontSize: buttonShowStyle.fontSize, 
						color: buttonShowStyle.color, backgroundImage: buttonShowStyle.backgroundImage, 
						borderTop: buttonShowStyle.borderTop, width: buttonShowStyle.width, 
						borderRadius: buttonShowStyle.borderRadius, color: buttonOneTitleStyle.color,
						fontWeight: buttonOneTitleStyle.fontWeight}"
						@tap="buttonOneClick(buttonOneClickParam)">{{buttonOneTitle}}</view>
						<!-- 按钮2 -->
					<view v-if="buttonTwoShow" class="sendBtn"
						:style="{fontFamily: buttonShowStyle.fontFamily, fontSize: buttonShowStyle.fontSize, 
						color: buttonShowStyle.color, backgroundImage: buttonShowStyle.backgroundImage, 
						borderTop: buttonShowStyle.borderTop, width: buttonShowStyle.width, borderRadius: buttonShowStyle.borderRadius }" 
						@tap="buttonTwoClick(buttonTwoClickParam)">{{buttonTwoTitle}}</view>
				</view>
				<view>
					<progress :percent="percent" stroke-width="10"></progress>
				</view>
			</view>
		</view>	
	</view>
</template>

<script>
	export default {
		name:"show-toast",
		data() {
			return {
				percent: 0,
			};
		},
		computed: {
			show(){
				return this.$toastStore.state.show;
			},
			title(){
				return this.$toastStore.state.title;
			},
			content(){
				return this.$toastStore.state.content;
			},
			icon(){
				return this.$toastStore.state.icon;
			},
			downLoadUrl(){
				return this.$toastStore.state.downLoadUrl;
			},
			toastCloseIconShow(){
				return this.$toastStore.state.toastCloseIconShow;
			},
			toastCloseIconStyle(){
				return this.$toastStore.state.toastCloseIconStyle;
			},
			buttonShow(){
				return this.$toastStore.state.buttonShow;
			},
			buttonOneShow(){
				return this.$toastStore.state.buttonOneShow;
			},
			buttonTwoShow(){
				return this.$toastStore.state.buttonTwoShow;
			},
			buttonOneTitleStyle(){
				return this.$toastStore.state.buttonOneTitleStyle;
			},
			toastBoxStyle(){
				return this.$toastStore.state.toastBoxStyle;
			},
			toastTitleSuccessStyle(){
				return this.$toastStore.state.toastTitleSuccessStyle;
			},
			toastTitleFailStyle(){
				return this.$toastStore.state.toastTitleFailStyle;
			},
			buttonShowStyle(){
				return this.$toastStore.state.buttonShowStyle;
			},
			buttonOneClickParam(){
				return this.$toastStore.state.buttonOneClickParam;
			},
			buttonTwoClickParam(){
				return this.$toastStore.state.buttonTwoClickParam;
			},
			successImgSrc(){
				return this.$toastStore.state.successImgSrc;
			},
			failImgClass(){
				return this.$toastStore.state.failImgClass;
			},
			successImgClass(){
				return this.$toastStore.state.successImgClass;
			},
			failImgSrc(){
				return this.$toastStore.state.failImgSrc;
			},
			buttonOneTitle(){
				return this.$toastStore.state.buttonOneTitle;
			},
			buttonTwoTitle(){
				return this.$toastStore.state.buttonTwoTitle;
			}
		},
		mounted() {
			setTimeout(()=>{
				this.$toastStore.commit('hideToast')
				this.$toastStore.commit('success',"confirm")
			},2000)
		},
		methods:{
			buttonOneClick(param){
				this.$toastStore.commit('buttonOneClick', {flag: param})
			},
			buttonTwoClick(param){
				this.$toastStore.commit('buttonTwoClick', {flag: param, url: this.downLoadUrl})
			},
			closeToast(){
				this.$toastStore.commit('hideToast')
			},
			clickBtn(res){
				this.$toastStore.commit('hideToast')
				this.$toastStore.commit('success',res)
			},
			moveHandle(){
				return;
			}
		},
		beforeDestroy(){
			this.$toastStore.commit('hideToast')
		},
	}
</script>

四、拓展阅读

相关文章