⚠️ Vue.extend是什么?
???? extend创建的是一个组件构造器,而不是一个具体的组件实例,需要通过Vue.components注册
????先来封装一个toast组件
toast.vue
<template>
<transition :name="fadeIn">
<div class="alertBox" v-show="show">
<div class="alert-mask" v-show="isShowMask"></div>
<transition :name="translate">
<div class="box" :class="position" v-show="show">
<p>{{text}}</p>
</div>
</transition>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
}
},
props: {
show: { // 是否显示此toast
default: false
},
text: { // 提醒文字
default: 'loading'
},
position: { // 提醒容器位置
default: 'center' //这里应该是middle 我没有改动画 所以暂时不用
},
isShowMask: { // 是否显示遮罩层
default: false
},
time: { // 显示时间
default: 1500
},
transition: { // 是否开启动画
default: true
}
},
mounted() { // 时间控制
// setTimeout(() => {
// this.show = false
// }, this.time)
},
computed: {
translate() { // 根据props,生成相对应的动画
if (!this.transition) {
return ''
} else {
if (this.position === 'top') {
return 'translate-top'
} else if (this.position === 'middle') {
return 'translate-middle'
} else if (this.position === 'bottom') {
return 'translate-bottom'
}
}
},
fadeIn() { // 同上
if (!this.transition) {
return ''
} else {
return 'fadeIn'
}
},
}
}
</script>
<style lang='scss'>
.box {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
min-width: 130px;
background: rgba(0, 0, 0, 0.5);
text-align: center;
color: #fff;
z-index: 5000;
color: #fff;
padding: 20px 30px;
border-radius: 10px;
word-wrap: normal ;
p {
display: inline-block;
text-align: left;
}
}
.box.top {
top: 50px;
margin-top: 150px;
}
.box.center {
top: 50%;
margin-top: -100px;
}
.box.bottom {
top: auto;
bottom: 50px;
margin-top: 0;
}
.alert-mask {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 4999;
}
.fadeIn-enter-active,
.fadeIn-leave-active {
transition: opacity 0.3s;
}
.fadeIn-enter,
.fadeIn-leave-active {
opacity: 0;
}
.translate-top-enter-active,
.translate-top-leave-active {
transition: all 0.3s cubic-bezier(0.36, 0.66, 0.04, 1);
}
.translate-top-enter,
.translate-top-leave-active {
transform: translateY(-50%);
opacity: 0;
}
.translate-middle-enter-active,
.translate-middle-leave-active {
transition: all 0.3s cubic-bezier(0.36, 0.66, 0.04, 1);
}
.translate-middle-enter,
.translate-middle-leave-active {
transform: translateY(80%);
opacity: 0;
}
.translate-bottom-enter-active,
.translate-bottom-leave-active {
transition: all 0.3s cubic-bezier(0.36, 0.66, 0.04, 1);
}
.translate-bottom-enter,
.translate-bottom-leave-active {
transform: translateY(100%);
opacity: 0;
}
</style>
复制代码
toast.js
import Alert from './toast.vue';
var Toast = {} // 定义插件对象
Toast.install = function (Vue, options) { // vue的install方法,用于定义vue插件
// 如果toast还在,则不再执行
if(document.getElementsByClassName('alertBox').length){
return
}
let toastTpl = Vue.extend(Alert) // 创建vue构造器
// el:提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标。可以是css选择器,也可以是HTMLElement实例。
// 在实例挂载之后,可以通过$vm.$el访问。
// 如果这个选项在实例化时有用到,实例将立即进入编译过程。否则,需要显示调用vm.$mount()手动开启编译(如下)
// 提供的元素只能作为挂载点。所有的挂载元素会被vue生成的dom替换。因此不能挂载在*元素(html, body)上
// let $vm = new toastTpl({
// el: document.createElement('div')
// })
let $vm = new toastTpl() // 实例化vue实例
// 此处使用$mount来手动开启编译。用$el来访问元素,并插入到body中
let tpl = $vm.$mount().$el
document.body.appendChild(tpl)
Vue.prototype.$toast = { // 在Vue的原型上添加实例方法,以全局调用
show(options) { // 控制toast显示的方法
if (typeof options === 'string') { // 对参数进行判断
$vm.text = options // 传入props
}
else if (typeof options === 'object') {
Object.assign($vm, options) // 合并参数与实例
}
$vm.show = true // 显示toast (防止每次弹窗创建一次新的定时器)
setTimeout(()=>{
$vm.show = false
},$vm.time) //消失时间
},
hide() { // 控制toast隐藏的方法
$vm.show = false
}
}
}
export default Toast;// 导出Toast(注意:此处不能用module exports导出,在一个文件中,不能同时使用require方式引入,而用module exports导出,两种方式不能混用)
复制代码
????使用方法
main.js
import Toast from './components/toast/toast';
Vue.use(Toast); //封装的messagebox组件 通过this调用
复制代码
home.vue 具体使用
this.$toast.show({
text: '复制成功',
time:'2000', //显示的时间
})
复制代码
???? 再来个confirm弹框
因为框架的风格不满足于项目的风格,所以有时候需要自己定制一个
????
messageBox.vue
<template>
<div class="dialog_views" v-show="isShowMessageBox" @touchmove.prevent>
<div class="UImask" @click="cancel"></div>
<transition name="confirm-fade">
<div class="UIdialog" v-show="isShowMessageBox">
<div class="UIdialog_hd">{{title}}</div>
<div class="UIdialog_bd">
<slot>{{content}}</slot>
</div>
<div :class="[ isShowCancelBtn ? 'UIdialog_ft' : 'UIdialog_ft UIdialog_ft_one']">
<span v-if="isShowCancelBtn" class="UIdialog_btn" @click="cancel">{{cancelVal}}</span>
<span v-if="isShowConfimrBtn" class="UIdialog_btn" @click="confirm">{{confirmVal}}</span>
</div>
</div>
</transition>
</div>
</template>
<script>
export default {
components: {
},
data() {
return {
isShowMessageBox: false,
resolve: '',
reject: '',
promise: '', // 保存promise对象
};
},
props: {
isShowConfimrBtn: {
type: Boolean,
default: true
},
content: {
type: String,
default: '这是弹框内容'
},
isShowCancelBtn: { //是否展示取消按钮
type: Boolean,
default: true
},
title: { //标题
type: String,
default: '提示',
},
confirmVal: {
type: String, //确认文字
default: '确定'
},
cancelVal: { //取消文字
type: String,
default: '取消'
},
maskHide: {
type: Boolean, //是否可以点击蒙层关闭
default: true
}
},
methods: {
// 确定,将promise断定为resolve状态
confirm() {
this.isShowMessageBox = false;
this.resolve('confirm');
this.remove();
},
// 取消,将promise断定为reject状态
cancel() {
this.isShowMessageBox = false;
this.reject('cancel');
this.remove();
},
// 弹出messageBox,并创建promise对象
showMsgBox() {
this.isShowMessageBox = true;
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
// 返回promise对象
return this.promise;
},
remove() {
setTimeout(() => {
this.destroy();
}, 100);
},
destroy() {
this.$destroy();
document.body.removeChild(this.$el);
},
}
};
</script>
<style lang='scss' scoped>
.UImask {
position: fixed;
z-index: 999;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
}
.UIdialog {
position: fixed;
z-index: 999;
width: 80%;
max-width: 400px;
display: table;
z-index: 5000;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
transition: opacity 0.3s, transform 0.4s;
text-align: center;
border-radius: 8px;
overflow: hidden;
background: #fff;
padding: 30px 40px;
}
.UIdialog_hd {
font-weight: bold;
font-size: 34px;
letter-spacing: 1px;
}
.UIdialog_bd {
margin: 40px 0;
text-align: center;
font-size: 24px;
slot,
p {
display: inline-block;
text-align: left;
}
}
.UIdialog_ft {
position: relative;
font-size: 28px;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
justify-content: space-between;
margin: 0 20px;
margin-bottom: -5px;
padding-top: 15px;
}
.UIdialog_btn {
display: block;
text-decoration: none;
position: relative;
display: block;
background-color: #000;
border-radius: 40px;
padding: 12px 45px;
}
.UIdialog_ft_one {
text-align: center;
justify-content: center;
}
/* 进入和出去的动画 */
.confirm-fade-enter-active {
animation: bounce-in 0.5s;
}
.confirm-fade-leave-active {
animation: bounce-in 0.5s reverse;
}
.confirm-fade-enter,
.confirm-fade-leave-to {
opacity: 0;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
}
</style>
复制代码
messageBox.js
import msgboxVue from './messageBox.vue';
// 定义插件对象
const MessageBox = {};
// vue的install方法,用于定义vue插件
MessageBox.install = function (Vue, options) {
const MessageBoxInstance = Vue.extend(msgboxVue);
let currentMsg;
const initInstance = () => {
// 实例化vue实例
currentMsg = new MessageBoxInstance();
let msgBoxEl = currentMsg.$mount().$el;
document.body.appendChild(msgBoxEl);
};
// 在Vue的原型上添加实例方法,以全局调用
Vue.prototype.$msgBox = {
showMsgBox (options) {
if (!currentMsg) {
initInstance();
}
if (typeof options === 'string') {
currentMsg.content = options;
} else if (typeof options === 'object') {
Object.assign(currentMsg, options);
}
// Object.assign方法只会拷贝源对象自身的并且可枚举的属性到目标对象
return currentMsg.showMsgBox()
.then(val => {
currentMsg = null;
return Promise.resolve(val);
})
.catch(err => {
currentMsg = null;
return Promise.reject(err);
});
}
};
};
export default MessageBox;
复制代码
????使用方法
main.js
import MessageBox from './components/messageBox/messageBox';
Vue.use(MessageBox); //封装的messagebox组件 通过this调用
复制代码
home.vue 具体使用
this.$msgBox.showMsgBox({
title: '提示',
content: '确定要删除吗',
}).then(async (val) => {
// ...
console.log('确认')
}).catch(() => {
// ...
console.log('取消')
});
复制代码
???? 这是我写项目中需要封装一个弹窗,样式可以自行修改