Vue3从0到1组件开发-系统组件:Notice提示

816 阅读3分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

转个弯

惯例:前言

原定计划到这一部分应该是讲业务组件了。

但是想来,业务组件本身和组件库的关系不算特别大,更多的是一种工作中的经验积累;其次,业务组件也是涉及到一些系统组件的,所以换个顺序,先说系统组件。再谈业务组件。

系统组件在我最初的设想里,主要是一些提供给项目展示信息的组件,例如交互反馈信息提示导航提示等。那么在这一部分中也将围绕几个常见的系统组件进行解析。

提示信息组件。

提示信息组件在移动端看的比较多,多见于应用内通知。

但是在PC端也是尤其用处的,以掘金为例,常见于bug的时候,或者错误信息通知

image.png

喏,就像掘金的这个信息,属于提醒作者的信息。

但是不得不吐槽一句,真的不友好,讲道理,这类提示不应该是输入框红框+附近文字提示的吗? 或者类似于下图。

image.png

当然,吐槽归吐槽,再怎么这也是一个比较好拿来距离的Notice组件了。

从布局开始的组件

既然要开始做了,自然是要先放下掘金,先考虑下常见的此类通知应该如何去设计。

在移动端的话,需要考虑三大交互: 从上到下的出现效果从下到上隐藏用户手动隐藏通知点击事件,此外就是标题内容图标

而在PC端时,内容版块不变,还是标题、内容、图标,并且除内容外应该都是可选的。出现隐藏方式可以自由设计。并且可以考虑当用户鼠标悬浮时,判断为用户正在查看内容,暂停隐藏的倒计时。

先看布局。

block content
  transition(
    name="notice"
    @before="beforeLeave"
    @leave="leave"
    @after="afterLeave"
    appear
  )
    div(
      class="yx-notice-content-rect" 
      v-if="isShow"
      @mouseover="endTime"
      @mouseout="startTime"
    )
      div.yx-notice-content
        div(
          class="yx-notice-bar"
          v-if="duration"
          :class="type"
        )
          i(:style="{width: bar + '%'}")
        span(
          class="yx-notice-icon"
          v-if="type"
        )
          i(:class="[icon || iconType[type], type]")
        div.yx-notice-title {{title}}
        div(
          class="yx-notice-description"
          v-if="content"
        ) {{content}}
        span(
          class="yx-notice-close"
          @click="close"
        )
          i.yx-icon-x

image.png

补充一下,其实左上角应该有success的icon,但是我懒得填充icon图标库,就没有了,大家脑补下。

再补充一下,右上角要有个关闭的按钮,同理,懒。嘻嘻嘻。

逻辑部分

逻辑部分,主要是完善刚刚说的几个交互的部分。

首先从隐藏说起,本身的出现隐藏没什么,css也能控制,主要是鼠标悬停时,停止倒计时。

// 控制当前通知的进度
const progress = () => {
  if(s <= t) {
    bar.value = (s/t) * 100
    s += 100
  } else {
    endTime()
    close()
  }
}
// 开始计时
const startTime = () => {
  if(props.duration > 0) time = setInterval(progress, 100)
}
// 暂停/结束计时
const endTime = () => {
  clearInterval(time)
  time = null;
}

关闭通知栏以及点击事件。

// 关闭通知
const close = () => {
  isShow.value = false;
}
const emits = defineEmits(['click'])
const handleClick = () => {
    // code
    
    emits('click')
}

最后

最后就是对事件的挂载了,这类组件一般挂载在全局。但挂载全局还是组件内,影响不大。

import element from './notice.vue';

let NoticeWrap;
// 生成dom 无需在template内另写
function createNoticeWrap(){
  const NoticeWrap = document.createElement('div');
  NoticeWrap.className = 'yx-notice-wrap';
  document.body.appendChild(NoticeWrap)
  return NoticeWrap
}
// 挂载到根节点
function createComponent(component, props){
  const vnode = h(component, props)
  render(vnode, document.createElement('div'))
  return vnode.component
}

function noticeCreate(props, type){
  if(!props.title){
    return 
  }
  if(type){
    props.type = type;
  }

  if(!NoticeWrap){
    NoticeWrap = createNoticeWrap()
  }

  const component = createComponent(element, props);
  NoticeWrap.appendChild(component.vnode.el)
}

function notice(props){
  noticeCreate(props)
}
// 不同的type,通过props传递给组件内。
;['info','error','success','warning'].forEach(type => {
  notice[type] = props => noticeCreate(props, type)
})

export default notice;

----------------------------
// 调用

// main.js

import notice from '@/components/notice/index';
app.config.globalProperties.$notice = notice;

// pages

this.$notice.info({
  title: 'Notice 通知',
  content: 'notice内容 ',
  // icon: '',
  // duration: ''
})

到此结束,快乐GG。