之前有個項目需要用到全局彈窗,而且公司項目的彈窗是有統(tǒng)一樣式的,雖然項目中引入了ElementUi組件,但還是不能滿足要求,所以就自己手動擼了兩個組件,一個是可以彈窗,需要用戶手動點擊確認(rèn)或關(guān)閉按鈕才能關(guān)閉的彈窗,另外一個是消息彈窗,出現(xiàn)問題會自動彈窗,過幾秒(可以自主調(diào))就會自動關(guān)閉。兩個組件大同小異,方法是一樣的,不過會有細(xì)小區(qū)別,不知道Vue.extend()方法的可以自己去Vue官網(wǎng)查看。話不多說,直接上代碼!
我是用vue+ts寫的,如果對ts不熟的直接褪去ts的入?yún)⑿r灳涂梢粤恕?br>
兩個組件的邏輯都是:
1.先創(chuàng)建一個組件,也就是彈窗的Dom結(jié)構(gòu)樣式以及需要用的數(shù)據(jù)等;
2.再創(chuàng)建一個js文件,引入剛剛創(chuàng)建的組件,利用Vue.extend()構(gòu)造一個子類實例,然后把你想包裝的數(shù)據(jù)和方法傳進(jìn)去,調(diào)用$mount()方法生成需要的DOM,然后插入body中
3.將第二步的方法掛載到Vue原型上,以保證在項目中可以直接調(diào)用
4.在項目main.js中引入,并用Vue.use進(jìn)行安裝,這樣就可以在項目中隨意使用了
項目結(jié)構(gòu)

image.png
全局提示框
組件
<template>
<div class="modalBox" v-show="visible">
<div class="modalBg"></div>
<div class="modalContainer">
<div class="modalheader">
彈窗
<span @click="doClose('close')">x</span>
</div>
<div class="modalContent">
<img v-bind:src="type === 'warning' ? warnImgSrc : confirmImgSrc" />
<div class="infoBox">
<div class="title">{{ title }}</div>
<div class="content">
<template v-if="typeof message !== 'string'">
<div v-for="(item, index) in message" :key="index">
{{ item }}
</div>
</template>
<div v-else>{{ message }}</div>
</div>
</div>
</div>
<!-- <div class="confirmBtn" @click="doClose('confirm')">確定</div> -->
<div class="modalFooter">
<div class="confirmBtn" @click="doClose('confirm')">確定</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Modal extends Vue {
// 初始數(shù)據(jù)可以直接聲明為實例的屬性
warnImgSrc = require('@/assets/images/warning.png')
confirmImgSrc = require('@/assets/images/success.png')
}
</script>
<style lang="scss" scoped>
.modalBox {
.modalBg {
position: fixed;
z-index: 3001;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.modalContainer {
max-width: 450px;
min-width: 350px;
max-height: 250px;
background: #ffffff;
position: fixed;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
z-index: 3002;
border-radius: 3px;
border: 1px solid #d9dbe2;
.modalheader {
width: 100%;
height: 30px;
line-height: 30px;
text-indent: 5px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background: #f2f3f4;
box-shadow: inset 0 -1px 0 0 #d9dbdf;
color: black;
font-weight: bold;
span {
font-size: 16px;
color: #999999;
display: block;
float: right;
margin-right: 5px;
cursor: pointer;
}
}
.modalContent {
margin-top: 10px;
padding: 0 10px;
// width: 100%;
height: 150px;
overflow-x: auto;
display: flex;
img {
width: 50px;
height: 50px;
margin-right: 10px;
}
.infoBox {
margin-top: 5px;
width: 100%;
padding-right: 10px;
.title {
background: #ffffff;
// margin-top: 8px;
font-size: 15px;
color: #000000;
text-align: left;
height: 15px;
line-height: 15px;
padding: 0px;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
word-break: break-all;
}
.content {
margin-top: 10px;
font-size: 15px;
color: #000000;
text-align: left;
/* line-height: 18px; */
padding: 0;
text-overflow: ellipsis;
div {
margin-top: 5px;
}
}
}
}
.confirmBtn {
// margin-left:153px;
// margin-top:25px;
margin: 0 auto;
width: 94px;
height: 29px;
margin-top: 10px;
border: 1px solid #4a90e2;
background: #c3e0ff;
border-radius: 2px;
line-height: 27px;
text-align: center;
font-size: 14px;
color: #000000;
cursor: pointer;
}
.modalFooter {
width: 100%;
height: 48px;
background: #f2f3f4;
box-shadow: inset 0 -1px 0 0 #d9dbdf;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
overflow: hidden;
}
}
}
準(zhǔn)備一個js/ts文件
import Modal from './index.vue'
import Vue from 'vue'
const MODAL = {
install(): void {
const VueModal = Vue.extend(Modal)
function modalAlert(modalObj: {
title: string
message: string | []
type: string
callback?: Record<string, any>
}) {
let vm = new VueModal({
el: document.createElement('div'),
data: {
title: modalObj.title,
message: modalObj.message,
type: modalObj.type,
visible: true
},
methods: {
doClose() {
vm.visible = false //修改變量
document.body.removeChild(vm.$el) //從body中移除
vm.$destroy() //銷毀
vm = null //垃圾回收
modalObj.callback &&
typeof modalObj.callback === 'function' &&
modalObj.callback()
}
}
})
document.body.appendChild(vm.$el)
}
// 掛載到vue原型上
Vue.prototype.$modalAlert = modalAlert
}
}
export default MODAL
在main.ts引入并注冊
import vModal from './components/Modal/main'
import vMessage from './components/Message/main'
Vue.use(vModal)
Vue.use(vMessage)
調(diào)用
this.$modalAlert({
title: '操作成功',
message: '撤單成功!',
type: 'warning',
callback() {
console.log('test')
}
})
效果

image.png
點擊確定后執(zhí)行回調(diào)函數(shù)

image.png
MESSAGE消息彈窗
基本是同樣的步驟
組件
<template>
<div class="messageBox" :class="type" v-show="visible">
<img v:bind:src="type === 'waring'?warnImgSrc : confirmImgSrc" />
<span>{{ message }}</span>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class Message extends Vue {
// 初始數(shù)據(jù)可以直接聲明為實例的屬性
warnImgSrc = require('@/assets/images/warning.png')
confirmImgSrc = require('@/assets/images/success.png')
message = ''
visible = false
}
</script>
<style lang="scss" scoped>
.messageBox {
position: fixed;
top: 40px;
text-align: center;
left: 50%;
transform: translateX(-50%);
min-width: 400px;
padding: 10px 20px;
color: #909399;
background: #f5f5f5;
font-size: 14px;
border-radius: 4px;
z-index: 99;
&.success {
color: #67c23c;
background: #f0f9eb;
}
&.info {
color: #909399;
background: #edf2fc;
}
&.error {
color: #f56c6c;
background: #fef0f0;
}
&.warning {
color: #e6a23c;
background: #fdf6ec;
}
}
</style>
準(zhǔn)備一個js/ts文件
import Message from './index.vue'
import Vue from 'vue'
const MESSAGE = {
duration: 3000, //顯示時間
install(): void {
const VueMessage = Vue.extend(Message)
function messageAlert(msgTxt: {
visible?: boolean
message: string
type: string
callback?: Record<string, any>
}) {
msgTxt.visible = true
const newMessage = new VueMessage({
data: msgTxt
})
let vm = newMessage.$mount()
document.body.appendChild(vm.$el) // 把生成的提示的dom插入body中
const timer = setTimeout(() => {
clearTimeout(timer)
vm.visible = false
document.body.removeChild(vm.$el) //從body中移除
vm.$destroy() //銷毀
vm = null //垃圾回收
msgTxt.callback &&
typeof msgTxt.callback === 'function' &&
msgTxt.callback()
}, MESSAGE.duration)
}
// 掛載到vue原型上,暴露四個方法
const messageTypeArr = ['info', 'success', 'error', 'warning']
Vue.prototype.$message = messageAlert
messageTypeArr.forEach(type => {
Vue.prototype.$message[type] = (content: string | {}) => {
const msgTxt =
typeof content === 'string'
? { type: type, message: content }
: content
return Vue.prototype.$message(msgTxt)
}
})
}
}
export default MESSAGE
在main.ts引入并注冊
import vModal from './components/Modal/main'
import vMessage from './components/Message/main'
Vue.use(vModal)
Vue.use(vMessage)
調(diào)用
//兩種調(diào)用方法
this.$message.error('測試')
this.$message({
type: 'success',
message: 'test',
callback() {
console.log('test')
}
})
效果
過3秒自動消失

image.png
帶回調(diào)函數(shù)的

image.png
消失后執(zhí)行回調(diào)函數(shù)

image.png
總結(jié)
基本上的方法都是一樣,不過根據(jù)各自彈窗的特點代碼會稍微有一些變化,樣式效果方面還有優(yōu)化的余地,繼續(xù)擼代碼去了~~~