前言
最近在做一个基于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,简单易懂,你上你也行!
根据自己的需求进行了进一步的修改,在此进行记录。第一次封装插件,只考虑实现没考虑性能及其他方面,还有很多方面的不足,如还有需要改进的地方,请您不吝赐教。