Vue3通知提醒框(Notification)

news2024/10/5 15:32:16

Vue3相关组件项目依赖版本信息

可自定义设置以下属性:

  • 消息的标题(title),默认'温馨提示'
  • 自动关闭的延时时长(duration),单位ms,默认4500ms
  • 消息从顶部弹出时,距离顶部的位置(top),单位像素px,默认24px
  • 消息从底部弹出时,距离底部的位置(bottom),单位像素px,默认24px
  • 弹出位置(placement),可选:左上topLeft,右上topRight(默认),左下bottomLeft,右下bottomRight

调用时可选以下五个方法对应五种不同样式:

  • notification.value.open(info) // 默认使用
  • notification.value.info(info) // info调用
  • notification.value.success(info) // success调用
  • notification.value.error(info) // error调用
  • notification.value.warn(info) // warn调用

五种样式效果如下图:

open()调用:

info()调用:

 success()调用:

error()调用:

warn()调用:

①创建通知提醒框组件Notification.vue:

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
// 以下两个方法是用 requestAnimationFrame 实现的等效 setTimeout() 和 clearTimeout()
import { rafTimeout, cancelRaf } from '../index'
const props = defineProps({
    title: { // 消息的标题
      type: String,
      default: '温馨提示'
    },
    duration: { // 自动关闭的延时时长,单位ms,默认4500ms;设置null时,不自动关闭
      type: Number,
      default: 4500
    },
    top: { // 消息从顶部弹出时,距离顶部的位置,单位像素px
      type: Number,
      default: 24
    },
    bottom: { // 消息从底部弹出时,距离底部的位置,单位像素
      type: Number,
      default: 24
    },
    placement: { // 消息弹出位置,可选topLeft,topRight,bottomLeft,bottomRight
      type: String,
      default: 'topRight'
    }
  })
enum ColorStyle { // 颜色主题对象
  info = '#1890FF',
  success = '#52c41a',
  error = '#f5222d',
  warn = '#faad14'
}
interface Notification {
  notification: string,
  mode: string
}
const resetTimer = ref()
const hideIndex = ref<number[]>([])
const hideTimers = ref<any[]>([])
const notificationData = ref<Notification[]>([])

const clear = computed(() => {
    // 所有提示是否已经全部变为false
    return hideIndex.value.length === notificationData.value.length
  })
watch(clear, (to, from) => { // 所有提示都消失后重置
  if (!from && to) {
    resetTimer.value = rafTimeout(() => {
      hideIndex.value.splice(0)
      notificationData.value.splice(0)
    }, 300)
  }
})
function onEnter (index: number) {
  cancelRaf(hideTimers.value[index])
  hideTimers.value[index] = null
}
function onLeave (index: number) {
  if (props.duration) {
    hideTimers.value[index] = rafTimeout(() => {
      onClose(index)
    }, props.duration)
  }
}
function show () {
  cancelRaf(resetTimer.value)
  hideTimers.value.push(null)
  const index = notificationData.value.length - 1
  if (props.duration) {
    hideTimers.value[index] = rafTimeout(() => {
      onClose(index)
    }, props.duration)
  }
}
function open (notification: string) {
  notificationData.value.push({
    notification,
    mode: 'open'
  })
  show()
}
function info (notification: string) {
  notificationData.value.push({
    notification,
    mode: 'info'
  })
  show()
}
function success (notification: string) {
  notificationData.value.push({
    notification,
    mode: 'success'
  })
  show()
}
function error (notification: string) {
  notificationData.value.push({
    notification,
    mode: 'error'
  })
  show()
}
function warn (notification: string) {
  notificationData.value.push({
    notification,
    mode: 'warn'
  })
  show()
}
defineExpose({
  open,
  info,
  success,
  error,
  warn
})
const emit = defineEmits(['close'])
function onClose (index: number) {
  hideIndex.value.push(index)
  emit('close')
}
</script>
<template>
  <div :class="['m-notification-wrap', placement]" :style="`top: ${placement.includes('top') ? top : ''}px; bottom: ${placement.includes('bottom') ? bottom : ''}px;`">
      <TransitionGroup name="slide-fade">
        <div
          class="m-notification"
          @mouseenter="onEnter(index)"
          @mouseleave="onLeave(index)"
          v-show="!hideIndex.includes(index)"
          v-for="(data, index) in notificationData"
          :key="index">
          <svg class="u-status-svg" v-if="data.mode==='info'" :fill="ColorStyle[data.mode]" viewBox="64 64 896 896" data-icon="info-circle" aria-hidden="true" focusable="false"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M464 336a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"></path></svg>
          <svg class="u-status-svg" v-if="data.mode==='success'" :fill="ColorStyle[data.mode]" viewBox="64 64 896 896" data-icon="check-circle" aria-hidden="true" focusable="false"><path d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0 0 51.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path></svg>
          <svg class="u-status-svg" v-if="data.mode==='warn'" :fill="ColorStyle[data.mode]" viewBox="64 64 896 896" data-icon="exclamation-circle" aria-hidden="true" focusable="false"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M464 688a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"></path></svg>
          <svg class="u-status-svg" v-if="data.mode==='error'" :fill="ColorStyle[data.mode]" viewBox="64 64 896 896" data-icon="close-circle" aria-hidden="true" focusable="false"><path d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 0 0-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"></path><path d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path></svg>
          <div :class="['u-title', {'mb4': data.mode!=='open', 'ml48': data.mode!=='open'}]">{{ title || '--' }}</div>
          <p :class="['u-description', {'ml48': data.mode!=='open'}]">{{ data.notification || '--' }}</p>
          <svg class="u-close" @click="onClose(index)" viewBox="64 64 896 896" data-icon="close" aria-hidden="true" focusable="false"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
        </div>
      </TransitionGroup>
  </div>
</template>
<style lang="less" scoped>
// 滑动渐变过渡效果
.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: all .3s ease;
}
.slide-fade-enter-from {
  transform: translateX(408px);
  -ms-transform: translateX(408px); /* IE 9 */
  -webkit-transform: translateX(408px); /* Safari and Chrome */
  opacity: 0;
}
.slide-fade-leave-to {
  opacity: 0;
}
.topRight {
  margin-right: 24px;
  right: 0;
}
.topLeft {
  margin-left: 24px;
  left: 0;
}
.bottomRight {
  margin-right: 24px;
  right: 0;
}
.bottomLeft {
  margin-left: 24px;
  left: 0;
}
.m-notification-wrap {
  position: fixed;
  z-index: 999; // 突出显示该层级
  width: 384px;
  color: rgba(0,0,0,.65);
  font-size: 14px;
  .m-notification {
    margin-bottom: 16px;
    padding: 16px 24px;
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 15%);
    line-height: 1.5;
    background: #fff;
    transition: all .3s;
    position: relative;
    .u-status-svg {
      width: 24px;
      height: 24px;
      display: inline-block;
      position: absolute;
      margin-left: 4px;
    }
    .u-title {
      padding-right: 24px;
      display: inline-block;
      margin-bottom: 8px;
      color: rgba(0,0,0,.85);
      font-size: 16px;
      line-height: 24px;
    }
    .u-description {
      font-size: 14px;
    }
    .mb4 {
      margin-bottom: 4px;
    }
    .ml48 {
      margin-left: 48px;
    }
    .u-close {
      display: inline-block;
      position: absolute;
      top: 21px;
      right: 24px;
      width: 14px;
      height: 14px;
      fill: rgba(0,0,0,.45);
      cursor: pointer;
      transition: fill .3s ease;
      &:hover {
        fill: rgba(0,0,0,.75);
      }
    }
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import { ref } from 'vue'
import Notification from './Notification.vue'

const notification = ref()

function onShow (info: string) {
  notification.value.open(info) // 默认使用
}
function onInfo (info: string) {
  notification.value.info(info) // 默认使用
}
function onSuccess (info: string) {
  notification.value.success(info) // success调用
}
function onError (info: string) {
  notification.value.error(info) // error调用
}
function onWarn (info: string) {
  notification.value.warn(info) // warning调用
}
function onClose () { // 点击默认关闭按钮时触发的回调函数
  console.log('关闭notification')
}
</script>
<template>
  <div>
    <Button @click="onShow('This is a normal notification')" class="mr30">Open</Button>
    <Button @click="onInfo('This is a normal notification')" class="mr30">Info</Button>
    <Button @click="onSuccess('This is a success notification')" class="mr30">Success</Button>
    <Button @click="onError('This is a error notification')" class="mr30">Error</Button>
    <Button @click="onWarn('This is a warn notification')" class="mr30">Warn</Button>
    <Notification
      ref="notification"
      placement="topRight"
      :duration="3000"
      :top="30"
      @close="onClose" />
  </div>
</template>
<style lang="less" scoped>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/424980.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【问题】开发遇到的小问题

文章目录使用糊涂工具&#xff0c;将时间字符串转化为LocalDateTime类型Date类型转换LocalDate类型jdk8 LocalDateTime获取当前时间和前后推时间echarts图中显示表格是需要添加宽高前端往后端传值时&#xff0c;需要转一下对象再往后端传使用 value-format"yyyy-MM-dd HH:…

jwt授权

JWT格式 由header、payload、signature三部分组成&#xff0c;中间用圆点(.)连接: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJpYW0uYXBpLm1hcm1vdGVkdS5jb20iLCJleHAiOjE2NDI4NTY2MzcsImlkZW50aXR5IjoiYWRtaW4iLCJpc3MiOiJpYW0tYXBpc2VydmVyIiwib3JpZ19pYXQiOjE2MzUw…

拼团系统开发|全民拼购商业模式解读

拼团系统开发|拼团模式在市场上已经屡见不鲜&#xff0c;某夕夕就是这方面的典范。不过现在市场上又出现一种升级版的拼团商业模式&#xff0c;也就是全民拼购&#xff0c;它除了可以帮助商家提升销量&#xff0c;还有引流裂变获客的效果&#xff0c;因此受到了许多企业商家的热…

chatgpt教你练习前端算法

今天想试试chatgpt关于代码算法这一块儿是否好用。 判断质数 上面的代码有一点小问题&#xff0c;当num为2时&#xff0c;返回的结果是错误的&#xff0c;我改进了一下&#xff0c;并优化了一点性能 // 判断是否是素数&#xff08;质数&#xff09; function isprime(number)…

【Linux】用LCD文字祝愿(Framebuffer+Freetype)

目录 前言 一、LCD操作原理 &#xff08;1&#xff09;LCD和Framebuffer。 &#xff08;2&#xff09;LCD的操作&#xff1a; &#xff08;3&#xff09;核心函数&#xff08;后续也会经常用到&#xff09; ①open函数 ②ioctl函数 ③mmap函数 二、字符的点阵显示 &a…

4K高清修复,模糊视频4k修复是怎么实现的?

在当今数字时代&#xff0c;高分辨率视频已成为大众观影的标配。4K分辨率作为其中高端的选项&#xff0c;提供了比传统1080p高出四倍的细节和清晰度&#xff0c;使得观众们能够更加身临其境地享受影视作品。然而&#xff0c;有时候我们可能会遇到4K视频质量不佳的问题&#xff…

Chapter3-用适合的方式发送和接收消息

3.1 不同类型的消费者 消费者可分为两种类型。 一个是DefaultMQPushConsumer &#xff0c;由系统控制读取操作&#xff0c;收到消息后自动调用传人的处理方法来处理&#xff1b;另 一个是 DefaultMlConsumer &#xff0c;读取操作中的大部分功能由使用者自主控制 。 3.1.1 Def…

uni-app:登录与支付--用户信息

用户信息 实现用户头像昵称区域的基本布局 在 my-userinfo 组件中&#xff0c;定义如下的 UI 结构&#xff1a; <template><view class"my-userinfo-container"><!-- 头像昵称区域 --><view class"top-box"><image src"…

如何通过小程序容器技术实现App的灰度发布

在当今移动应用市场竞争激烈的环境下&#xff0c;如何更快地发布新版本、更精确地测试和调整、更好地了解用户需求和行为&#xff0c;成为了每个App开发者面临的重要挑战。在这个背景下&#xff0c;灰度发布和小程序容器技术成为了越来越受欢迎的解决方案。 灰度发布是指将新版…

【Linux高性能服务器编程】I/O复用的高级应用

文章目录一、基于 select 的非阻塞 connect二、基于 poll 的聊天室程序2.1 客户端2.2 服务器三、基于 epoll 实现同时处理 TCP 和 UDP 服务一、基于 select 的非阻塞 connect connect系统调用的 man 手册中有如下一段内容&#xff1a; EINPROGERESS The socket is nonblocking…

ChatGPT既然这么火,有没有弊端呢?

介绍 在现代社会中&#xff0c;人们越来越依赖技术来解决问题。聊天机器人是一种最新的技术趋势&#xff0c;这种技术可以为人们带来很多便利。而ChatGPT聊天机器人则是其中的一种&#xff0c;它使用了大型的语言模型GPT&#xff08;Generative Pre-trained Transformer&#…

实操干货丨ChatGPT+XMind,效率爆炸,天下无敌

日常办公中&#xff0c;常用工具我们最熟悉的莫过于office铁三角&#xff1a;Word/Excel/PPT&#xff0c;除了这哥仨&#xff0c;再让你推荐一个。 XMind&#xff0c;绝对位列其中。 今天给大家分享一个 ChatGPTXMind的绝佳玩法&#xff0c;不需要下载安装任何额外的工具。 …

【MySQL | 基础篇】05、MySQL 事务详解

目录 一、事务简介 二、事务操作 2.1 未控制事务 2.2 控制事务一 2.3 控制事务二 三、事务四大特性 四、并发事务问题 五、事务隔离级别 六、并发事务演示 6.1 脏读演示 6.2 不可重复读演示 6.3 幻读演示 一、事务简介 事务是一组操作的集合&#xff0c;它是一个不…

【云原生】Kubernetes 中容器跨主机网络是怎么样的?

文章目录前言什么是 FlannelFlannel 的后端实现有哪些UDPVXLANHost-gw基于 Flannel UDP 模式的实现跨主通信UDP 模式案例实现基于 Flannel VXLAN 模式的跨主通信VXLAN 模式案例实现总结前言 在云原生领域&#xff0c;Kubernetes 已经成为了最主流的容器管理工具。Kubernetes 支…

JMM内存模型详解

1、概要 JMM全称叫Java Memory Model&#xff08;Java内存模型&#xff09;&#xff0c;什么是JMM&#xff0c;为什么要设置JMM&#xff0c;要弄清楚这个&#xff0c;咱们要先从计算机硬件存储体系说起。 2、计算机硬件存储体系 Window任务管理器Mac资源管理器window和mac是两…

Flink 优化 (二) --------- 状态及 Checkpoint 调优

目录一、RocksDB 大状态调优1. 开启 State 访问性能监控2. 开启增量检查点和本地恢复3. 调整预定义选项4. 增大 block 缓存5. 增大 write buffer 和 level 阈值大小6. 增大 write buffer 数量7. 增大后台线程数和 write buffer 合并数8. 开启分区索引功能9. 参数设定案例二、Ch…

拐点!智能座舱破局2023

“这是我们看到的整个座舱域控渗透率&#xff0c;2022年是8.28%&#xff0c;主力的搭载车型仍然是30-35万区间。”3月29日&#xff0c;2023年度&#xff08;第五届&#xff09;高工智能汽车市场峰会上&#xff0c;高工智能汽车研究院首发《2022-2025年中国智能汽车产业链市场数…

WRF中替换LAI数据

WRF中替换LAI数据 本次下载的LAI数据是GLASS中的AVHRR(1981-2018)(V50)&#xff0c;可以从这个这里获取数据GLASS_AVHRR_LAI数据集。由于我需要做一个时间序列的模拟&#xff0c;总共下载了从1990-2018年灌溉季3-9月份对应的天数为73&#xff0c;105&#xff0c;137&#xff0…

人工智能大时代——AIGC综述

生成式AI分类 模型按照输入输出的数据类型分类&#xff0c;目前主要包括9类。 有趣的是&#xff0c;在这些已发布大模型的背后&#xff0c;只有六个组织&#xff08;OpenAI, Google, DeepMind, Meta, runway, Nvidia&#xff09;参与部署了这些最先进的模型。 其主要原因是&am…

11.基于粒子群算法的含风光燃储微网优化调度(论文复现)

说明书 相关代码资源&#xff1a;基本算法智能微电网粒子群优化算法,微源&#xff1a;光伏、风机、发电机、储能等 基于多目标算法的冷热电联供型综合能源系统运行优化 基于多目标粒子群算法冷热电联供综合能源系统运行优化 MATLAB代码&#xff1a;基于粒子群算法的含风光燃…