之前有个项目需要用到全局弹窗,而且公司项目的弹窗是有统一样式的,虽然项目中引入了ElementUi组件,但还是不能满足要求,所以就自己手动撸了两个组件,一个是可以弹窗,需要用户手动点击确认或关闭按钮才能关闭的弹窗,另外一个是消息弹窗,出现问题会自动弹窗,过几秒(可以自主调)就会自动关闭。两个组件大同小异,方法是一样的,不过会有细小区别,不知道Vue.extend()方法的可以自己去Vue官网查看。话不多说,直接上代码!
我是用vue+ts写的,如果对ts不熟的直接褪去ts的入参校验就可以了。
两个组件的逻辑都是:
1.先创建一个组件,也就是弹窗的Dom结构样式以及需要用的数据等;
2.再创建一个js文件,引入刚刚创建的组件,利用Vue.extend()构造一个子类实例,然后把你想包装的数据和方法传进去,调用$mount()方法生成需要的DOM,然后插入body中
3.将第二步的方法挂载到Vue原型上,以保证在项目中可以直接调用
4.在项目main.js中引入,并用Vue.use进行安装,这样就可以在项目中随意使用了
项目结构
全局提示框
组件
<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 {
// 初始数据可以直接声明为实例的属性
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;
}
}
}
准备一个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)
调用
this.$modalAlert({
title: '操作成功',
message: '撤单成功!',
type: 'warning',
callback() {
console.log('test')
}
})
效果
点击确定后执行回调函数
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 {
// 初始数据可以直接声明为实例的属性
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>
准备一个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)
调用
//两种调用方法
this.$message.error('测试')
this.$message({
type: 'success',
message: 'test',
callback() {
console.log('test')
}
})
效果
过3秒自动消失
带回调函数的
消失后执行回调函数
总结
基本上的方法都是一样,不过根据各自弹窗的特点代码会稍微有一些变化,样式效果方面还有优化的余地,继续撸代码去了~~~