欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

✨vue封装一个全局toast和confirm(Vue.extend)

程序员文章站 2022-03-11 21:40:37
...

⚠️ 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('取消')
      });
复制代码

???? 这是我写项目中需要封装一个弹窗,样式可以自行修改

转载于:https://juejin.im/post/5cde2f5bf265da1b9253a82f