vue自定义插件--右上角通知框,随任务的轮询结束可自动关闭

前言

最近在做一个基于vue的云控制台的项目,其中包括很多创建主机、创建云盘等功能,但是创建需要一个时间,得做一个正在创建中的消息通知窗进行展示来告知客户,同时随任务的结束,通知窗显示成功失败,并可以自动隐藏或者手动关闭。
elementui里有一个Notification通知插件,但是有好多地方不符合我的需求,于是查阅资料自己封装一个。

业务逻辑

总结一下业务逻辑:拿创建云盘来说,当表单输入完成,点击创建时,会向后台发送请求,接口会返回操作是否成功以及成功后的taskId列表,这时弹窗在右上角出现,并有loading效果。接下来我会用拿taskId每隔2s去轮询,一直到结果是任务成功或者失败了,轮询结束,弹窗展示轮询结果。
点击创建后的返回数据格式:

{
    
    
	data:{
    
    
		tasks:[
			{
    
    uuid:'xxxx',name:'创建xx云盘'},
			{
    
    uuid:'xxxx',name:'加载xx云盘'},
		]
	}
}

轮询任务的返回数据格式:

{
    
    
	data:{
    
    
		message:'任务进行中',status:2
	}
}

效果如下:

任务正在进行:
在这里插入图片描述
任务成功:
在这里插入图片描述
任务失败:
在这里插入图片描述

实现

文件目录

在这里插入图片描述

代码

1、notify.vue

这里定义了几种类型的弹窗,并用transition来过渡,并且定义了点 x 手动关闭的方法。

<template>
  <transition name="slide-fade">
    <div class="my-notify" :name="name" v-if="notifyFlag">
      <span class="close" @click="close"> X </span>  
      <div class="notify success" v-if="type=='success'">
        <img src="../../assets/img/common/success.gif" alt="">
        <div class="content"> {
    
    {
    
    message}}</div>
      </div>
      <div class="notify loading" v-else-if="type=='loading'">
        <img src="../../assets/img/common/loading.gif" alt="">
        <div class="content">{
    
    {
    
    message}}</div>
      </div>
      <div class="notify error" v-else-if="type=='error'">
        <img src="../../assets/img/common/error.png" alt="">
        <div class="content">{
    
    {
    
    message}}</div>
      </div>
      <div class="notify warning" v-else-if="type=='warning'">
        <img src="../../assets/img/common/error.png" alt="">
        <div class="content">{
    
    {
    
    message}}</div>
      </div>
    </div>
  </transition>
</template>

<script>


export default {
    
    
  name: 'vue-notify',
  data() {
    
    
    return {
    
    
      notifyFlag:false,
      type:'loading'
    };
  },
  methods:{
    
    
      close(){
    
    
        this.notifyFlag = false;
      }
  }
  
  
};
</script>

<style>
.slide-fade-leave-active {
    
    
  transition: all .2s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to{
    
    
  transform: translateX(10px);
  opacity: 0;
}
.my-notify{
    
    
  /* margin: 10px; */
  width: 350px;
  background-color: #f2f5fc;
  border: 1px solid #c4d5ff;
  padding: 9px 10px;
  border-radius:3px;
  margin-top:10px;
  position:relative;  
  margin-right: 19px;
}
.my-notify:hover{
    
    
  border-color:#a9c1fb;  
}
.my-notify .close{
    
    
      position: absolute;
      left:325px;top:7px;
      font-size: 14px;
      z-index: 100;
      cursor: pointer;
      color:#c7c4c4;
  }
  .my-notify .close:hover{
    
    
    color:#aaa;
  }
.notify{
    
    
  position: relative;
  right: 10px;
  padding-left: 10px;
  width: 320px;
  border-radius: 4px;
  background-color:#f2f5fc;    
}
.notify img{
    
    
  display: inline-block;    
  /* vertical-align: text-top; */
  width: 20px;
  margin-right: 7px;
}
.notify .content{
    
    
  width: 270px;
  font-size: 14px;
  vertical-align: top;
  display: inline-block;
}
@keyframes show{
    
    
  0%{
    
    
    right: -350px;
  }
  100%{
    
    
    right: 0;
  }
}

</style>

2、index.js

import vue from 'vue'
import myNotify from './notify.vue'
 
// 创建vue组件实例
const notify = vue.extend(myNotify);

//声明构造函数变量
let func={
    
    };

//添加通知节点(用来存放通知的元素)
let notifyWrap = document.createElement('div');
notifyWrap.className = "notify-wrap"
notifyWrap.style = "position: fixed; right: 0px; top: 65px; transition-duration: .5s;"
document.body.appendChild(notifyWrap);

let NotifyMessage = (options) => {
    
    

  let {
    
    name,message,type} = options;
  
    func['instance'+options.name] = new notify();
    func['instance'+options.name].vm = func['instance'+options.name].$mount();
    //往notifyWrap里面添加通知
    notifyWrap.appendChild(func['instance'+options.name].vm.$el);
    
    func['instance'+options.name].name = name;
    func['instance'+options.name].message = message;
    func['instance'+options.name].type = type;

    func['instance'+options.name].notifyFlag = true;

}
//关闭
NotifyMessage.close = (options) => {
    
    
  let {
    
    name} = options;

  func['instance'+options.name].notifyFlag = false
}
//当成功或者失败时候修改内容和type
NotifyMessage.modify = (options) => {
    
    
  if(typeof options === "object"){
    
    
    let {
    
    name,message,type} = options;
    func['instance'+options.name].message = message;
    func['instance'+options.name].type = type;
  }else{
    
    
    return
  }
}
NotifyMessage.install = (vue) => {
    
    
  vue.prototype.$notifyMessage = NotifyMessage;
}
 
export default NotifyMessage;

3、main.js里注册

import NotifyMessage from '@/plugin/notify/index' 
Vue.use(NotifyMessage)

这个时候就可以在页面里用了,用法为

NotifyMessage({
    
    
      name:"xxx",
      message:task.name,
      type:'loading'
    })
    
NotifyMessage.modify({
    
     //修改
      name:"xxx",
      message:task.name,
      type:'success'
    })
    
NotifyMessage.close({
    
    name:'xxx'})//关闭

因为这个项目需要每隔2s去轮询任务结果自动关闭,所以进行进一步的封装。

4、pollingTask.js

import * as commonApi from "@/api/common";
import  NotifyMessage  from '@/plugin/notify/index'

export default function checkTask(options){
    
    
    let task = options;
    NotifyMessage({
    
    
      name:"notify"+task.uuid,
      message:task.name,
      type:'loading'
    })
    let taskId = task.uuid;

    return new Promise((resolve, reject) => {
    
    

      window['diskTimer'+taskId] = window.setInterval(() => {
    
    
        commonApi.checkTask({
    
    uuid:taskId}).then(res => {
    
    
          if (res.code == 200) {
    
    
            if(res.data.status == 1){
    
    //1.进行中 
              return;
            }else if(res.data.status == 2){
    
    //2.成功
              window.clearInterval(window['diskTimer'+taskId]);//清除定时器
              NotifyMessage.modify({
    
    
                name:"notify"+taskId,
                message:task.name,
                type:'success'
              })
              setTimeout(()=>{
    
    
                NotifyMessage.close({
    
    name:'notify'+taskId});//关闭notice
              },2000)
              resolve('任务成功');
            }else{
    
    //3.失败
              window.clearInterval(window['diskTimer'+taskId]);//清除定时器
              NotifyMessage.modify({
    
    
                name:"notify"+taskId,
                message:res.data.message,
                type:'error'
              })
              //任务失败可以选择保持弹窗不关闭,如果要自动关闭则解开代码
              // setTimeout(()=>{
    
    
              //   NotifyMessage.close({name:'notify'+taskId});
              // },3000)
              reject('任务失败');
            }
          } else {
    
    
            window.clearInterval(window['diskTimer'+taskId]);
            NotifyMessage.modify({
    
    
              name:"notify"+taskId,
              message:task.name+'失败',
              type:'error'
            })
            setTimeout(()=>{
    
    
              NotifyMessage.close({
    
    name:'notify'+taskId});
            },3000)
            this.$message({
    
    
              type: "error",
              message: res.msg
            });
            reject('任务失败');
          }
        })
        .catch(err => {
    
    
          console.log(err);
          reject('任务失败');
        });
      }, 2000);
    })
  }

这里用了周期性定时器,每隔2s去调用接口看任务是否完成。因为任务完成后要自动刷新页面或者进行进一步的操作,所以这里新建了promise实例来等待任务完成,然后去进行其他操作。

5、再次在main.js里进行注册

//轮询操作任务是否完成
import checkTask from '@/utils/pollingTask';
Vue.prototype.$checkTask=checkTask;

6、页面里用法

// param = {uuid:'xxxxx',name:'创建xx云盘',}
this.$checkTask(param).then(()=>{
    
    
       //刷新列表或者其他操作
});

至此,我的需求已经基本实现。

后记

本篇文章是参考至文章vue 如何写一个消息通知组件$notify,简单易懂,你上你也行!
根据自己的需求进行了进一步的修改,在此进行记录。第一次封装插件,只考虑实现没考虑性能及其他方面,还有很多方面的不足,如还有需要改进的地方,请您不吝赐教。

猜你喜欢

转载自blog.csdn.net/qq_39352780/article/details/107515792
今日推荐