背景
为了解决uni-app 任意位置出现弹窗
解决方案
一、最初方案
受限于uni-app 调用组件需要每个页面都引入注册才可以使用,此方案繁琐,每个页面都要写侵入性比较强
二、改进方案
app端:新建一个页面进行跳转,可以实现伪弹窗(其实是打开一个背景透明的页面)
web端: 全局挂载body 插入一个弹窗
三、初步实现方案
就是 利用条件编译,web端写组件、app 端写页面,利用不同的生命周期,完成通用的逻辑
四、详细实现方案
1、完成弹窗页面开发
统一暴露一个show 方法,app 端使用 onload 监听事件触发,web端使用 show 方法触发
<template>
<u-popup id="globalPopup-box" :show="params.show" :mode="params.mode" bgColor="transparent"
@close="cancelSubmit(true)">
<template v-if="params.type === 1">
<view class="globalPopupContent" :class="{ globalPopupContentCenter: params.mode == 'center' }">
<view class="title">{{ params.title }}</view>
</view>
</template>
<template v-else>
<view class="globalPopupContent" :class="{ globalPopupContentCenter: params.mode == 'center' }">
<view class="title">{{ params.title }}</view>
</view>
</template>
</u-popup>
</template>
<script>
export default {
name: "globalPopup",
data() {
return {
eventChannel: null,
params: {},
}
},
// #ifdef APP-PLUS
onLoad() {
this.eventChannel = this.getOpenerEventChannel();
this.eventChannel.on('globalPopup', (data) => {
console.log('globalPopup:', data)
this.init(data)
})
},
// #endif
methods: {
init(data) {
this.params = data
// 通用逻辑写在这里
},
// #ifdef H5
show(data) {
console.log('H5globalPopup:', data)
this.init(data)
},
// #endif
cancel() {
// #ifdef APP-PLUS
let _this = this;
uni.navigateBack({
delta: 1,
success() {
_this.eventChannel.emit('cancel');
}
})
// #endif
this.hide()
// #ifdef APP-PLUS
this.params.cancel && this.params.cancel();
// #endif
},
confirm() {
// #ifdef APP-PLUS
let _this = this;
uni.navigateBack({
delta: 1,
success() {
_this.eventChannel.emit('confirm');
}
})
// #endif
this.hide();
// #ifdef APP-PLUS
this.params.confirm && this.params.confirm();
// #endif
},
hide() {
setTimeout(() => {
this.params = {}
}, 100)
}
}
}
</script>
<style>
page {
background: transparent;
}
</style>
2、页面路由配置
,{
"path" : "components/globalPopup/globalPopup",
"style": {
"navigationStyle": "custom",
"backgroundColor": "transparent",
"app-plus": {
"animationType": "fade-in",
"background": "transparent",
"popGesture": "none",
"bounce": "none",
"titleNView": false
}
}
},
3、main.js中全局挂载 globalPopup.js
globalPopup.js
const install = Vue => {
Vue.prototype.$globalPopup = {
show(params) {
let pointPageUrl = getCurrentPages()[getCurrentPages().length - 1].route;
if (pointPageUrl == 'components/globalPopup/globalPopup') return
uni.navigateTo({
url: '/components/globalPopup/globalPopup',
success: function (res) {
// 利用事件 通知 目标页面
res.eventChannel.emit('globalPopup', params)
}
})
}
}
}
export default install;
main.js 的编码 条件编译
// #ifdef APP-PLUS
import globalPopupjs from '@/components/globalPopup/globalPopup.js';
Vue.use(globalPopupjs);
// #endif
// #ifdef H5
import globalPopup from '@/components/globalPopup/globalPopup.vue'
const PopupVue = Vue.extend(globalPopup);
const popupDom = new PopupVue();
Vue.prototype.$globalPopup = popupDom.$mount();
const lastEl = document.body.lastElementChild;
if (lastEl.id !== 'globalPopup-box') {
setTimeout(() => {
document.body.appendChild(Vue.prototype.$globalPopup.$el)
}, 0)
}
// #endif
4、如何任意位置出现弹窗
利用接口触发,返回相关弹窗配置
接口触发逻辑
if (data.pop) {
uni.$emit('showMyPopup', data.pop)
}
监听逻辑
// 监听事件
uni.$on('showMyPopup', (pop) => {
if (!this.isShowGlobalPopup) {
console.log(pop, 'showMyPopup')
let {
userQuestionStyleValue, // 样式值 1底部弹窗 2页中弹窗
userQuestionTemplateValue, // 模板值 1是否类 2打分类,
userQuestionInfo,
userQuestionAnswerDTO,
} = pop
this.$globalPopup.show({
id: pop.id,
show: true,
type: userQuestionTemplateValue == '1' ? 1 : 2,
userQuestionInfo,
title: userQuestionInfo.questionName,
userQuestionAnswerDTO,
mode: userQuestionStyleValue == '1' ? 'bottom' : 'center'
})
}
});