Appearance
uniapp命令式弹窗组件v2版
uniapp的命令式弹窗组件和vue的有点不一样,vue的是挂载到body上的,uniapp跟小程序一样是没有body的
components/MessageBox/MessageBox.vue
组件结构文件
vue
<template>
<view class="message-box" v-if="visible">
<view class="mask" @click="handleMaskClick"></view>
<view class="content">
<view class="header" v-if="title">{{ title }}</view>
<view class="body">{{ content }}</view>
<view class="footer">
<button
v-if="showCancel"
class="btn cancel"
@click.stop="handleCancel"
>{{ cancelText }}</button>
<button
class="btn confirm"
@click.stop="handleConfirm"
>{{ confirmText }}</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'message-box',
data() {
return {
visible: false,
title: '',
content: '',
showCancel: true,
cancelText: '取消',
confirmText: '确定',
resolve: null,
reject: null
}
},
methods: {
show(options) {
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
const {
title = '',
content = '',
showCancel = true,
cancelText = '取消',
confirmText = '确定'
} = options
this.title = title
this.content = content
this.showCancel = showCancel
this.cancelText = cancelText
this.confirmText = confirmText
this.visible = true
})
},
handleConfirm() {
if (this.resolve) {
this.resolve({ confirm: true, cancel: false })
}
this.visible = false
},
handleCancel() {
if (this.resolve) {
this.resolve({ confirm: false, cancel: true })
}
this.visible = false
},
handleMaskClick() {
if (this.showCancel) {
this.handleCancel()
}
}
}
}
</script>
<style lang="scss" scoped>
.message-box {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.content {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 600rpx;
background: #fff;
border-radius: 8rpx;
overflow: hidden;
}
.header {
width: 100%;
padding: 30rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
border-bottom: 1rpx solid #eee;
}
.body {
width: 100%;
padding: 30rpx;
text-align: center;
font-size: 28rpx;
color: #666;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.footer {
width: 100%;
display: flex;
border-top: 1rpx solid #eee;
.btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 28rpx;
background: #fff;
border: none;
border-radius: 0;
&::after {
border: none;
}
&.cancel {
color: #666;
border-right: 1rpx solid #eee;
}
&.confirm {
color: $uni-color-primary;
}
}
}
}
</style>
<template>
<view class="message-box" v-if="visible">
<view class="mask" @click="handleMaskClick"></view>
<view class="content">
<view class="header" v-if="title">{{ title }}</view>
<view class="body">{{ content }}</view>
<view class="footer">
<button
v-if="showCancel"
class="btn cancel"
@click.stop="handleCancel"
>{{ cancelText }}</button>
<button
class="btn confirm"
@click.stop="handleConfirm"
>{{ confirmText }}</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'message-box',
data() {
return {
visible: false,
title: '',
content: '',
showCancel: true,
cancelText: '取消',
confirmText: '确定',
resolve: null,
reject: null
}
},
methods: {
show(options) {
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
const {
title = '',
content = '',
showCancel = true,
cancelText = '取消',
confirmText = '确定'
} = options
this.title = title
this.content = content
this.showCancel = showCancel
this.cancelText = cancelText
this.confirmText = confirmText
this.visible = true
})
},
handleConfirm() {
if (this.resolve) {
this.resolve({ confirm: true, cancel: false })
}
this.visible = false
},
handleCancel() {
if (this.resolve) {
this.resolve({ confirm: false, cancel: true })
}
this.visible = false
},
handleMaskClick() {
if (this.showCancel) {
this.handleCancel()
}
}
}
}
</script>
<style lang="scss" scoped>
.message-box {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.content {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 600rpx;
background: #fff;
border-radius: 8rpx;
overflow: hidden;
}
.header {
width: 100%;
padding: 30rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
border-bottom: 1rpx solid #eee;
}
.body {
width: 100%;
padding: 30rpx;
text-align: center;
font-size: 28rpx;
color: #666;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.footer {
width: 100%;
display: flex;
border-top: 1rpx solid #eee;
.btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 28rpx;
background: #fff;
border: none;
border-radius: 0;
&::after {
border: none;
}
&.cancel {
color: #666;
border-right: 1rpx solid #eee;
}
&.confirm {
color: $uni-color-primary;
}
}
}
}
</style>
utils/MessageBox.js
挂载结构
js
import Vue from 'vue'
import MessageBoxComponent from '../components/MessageBox/MessageBox.vue'
let messageBoxInstance = null
const initInstance = () => {
const MessageBoxConstructor = Vue.extend(MessageBoxComponent)
messageBoxInstance = new MessageBoxConstructor()
// #ifdef H5
const parent = document.createElement('div')
parent.id = 'message-box-wrapper'
messageBoxInstance.$mount(parent)
document.body.appendChild(messageBoxInstance.$el)
// #endif
// #ifndef H5
messageBoxInstance.$mount() // 小程序中直接挂载
// #endif
}
const MessageBox = {
show(options) {
if (!messageBoxInstance) {
initInstance()
}
return messageBoxInstance.show(options)
}
}
export default MessageBox
import Vue from 'vue'
import MessageBoxComponent from '../components/MessageBox/MessageBox.vue'
let messageBoxInstance = null
const initInstance = () => {
const MessageBoxConstructor = Vue.extend(MessageBoxComponent)
messageBoxInstance = new MessageBoxConstructor()
// #ifdef H5
const parent = document.createElement('div')
parent.id = 'message-box-wrapper'
messageBoxInstance.$mount(parent)
document.body.appendChild(messageBoxInstance.$el)
// #endif
// #ifndef H5
messageBoxInstance.$mount() // 小程序中直接挂载
// #endif
}
const MessageBox = {
show(options) {
if (!messageBoxInstance) {
initInstance()
}
return messageBoxInstance.show(options)
}
}
export default MessageBox
页面调用
vue
<template>
<view class="content">
<button @click="showMessage">显示弹窗</button>
<message-box ref="messageBox"></message-box>
</view>
</template>
<script>
import MessageBox from '../../components/MessageBox/MessageBox.vue'
export default {
components: {
'message-box': MessageBox
},
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
async showMessage() {
try {
setTimeout(async () => {
if (this.$refs.messageBox) {
const res = await this.$refs.messageBox.show({
title: '提示',
content: '这是一个自定义弹窗',
confirmText: '确定',
cancelText: '取消',
showCancel: true
})
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
} else {
console.error('MessageBox ref not found')
}
}, 100)
} catch (error) {
console.error('MessageBox error:', error)
}
}
}
}
</script>
<template>
<view class="content">
<button @click="showMessage">显示弹窗</button>
<message-box ref="messageBox"></message-box>
</view>
</template>
<script>
import MessageBox from '../../components/MessageBox/MessageBox.vue'
export default {
components: {
'message-box': MessageBox
},
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
async showMessage() {
try {
setTimeout(async () => {
if (this.$refs.messageBox) {
const res = await this.$refs.messageBox.show({
title: '提示',
content: '这是一个自定义弹窗',
confirmText: '确定',
cancelText: '取消',
showCancel: true
})
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
} else {
console.error('MessageBox ref not found')
}
}, 100)
} catch (error) {
console.error('MessageBox error:', error)
}
}
}
}
</script>