uniapp实现顶部弹窗通知,支持H5、App端

发布时间 2023-05-30 11:55:35作者: Stitchhhhh

文件结构:

 h5_push.vue

<template>
  <view>
    <transition name="slide-fade">
      <view class="h5_push" v-if="show" @tap="handleClick" :style="style">
        <view class="push-title">
          <view class="push-type">
            <image src="/static/message-icon.png"></image>
            {{ messageType }}
          </view>
          {{ messageTime }}
        </view>
        <view class="push-body">
          <view class="push-content">
            <view class="push-content-title">{{ messageTitle }}</view>
            <view class="push-content-text">{{ messageContent }}</view>
          </view>
          <image :src="messageImage" class="push-img" mode="aspectFill"></image>
        </view>
      </view>
    </transition>
  </view>
</template>

<script>
export default {
  data() {
    return {
      show: false,
      // 关闭时间
      closeTime: 3000,
      // app内横幅提醒
      inApp: false,
      // 声音提醒
      voice: true,
      // 振动提醒
      vibration: false,
      // 消息分类
      messageType: '',
      // 通知标题
      messageTitle: '',
      // 时间
      messageTime: '现在',
      // 通知文案
      messageContent: '',
      // 缩略图
      messageImage: '',
      tap: () => {},
      top: 20,
      left: 20,
    }
  },
  computed: {
    style() {
      let system = uni.getSystemInfoSync()
      let statusBarHeight = system.statusBarHeight
      return `top: calc(${statusBarHeight}px + ${this.top}rpx);left: ${this.left}rpx`
    },
  },
  created() {
    setTimeout(() => {
      this.show = false
    }, this.closeTime)
  },
  methods: {
        handleClick() {
            this.tap()
            this.show = false
        }
    },
}
</script>

<style lang="scss" scoped>
.h5_push {
  width: 710rpx;
  height: 192rpx;
  background: #ffffff;
  box-shadow: 0 3rpx 18rpx 0 rgba(54, 58, 68, 0.08);
  border-radius: 20rpx;
  position: fixed;
  z-index: 9999999;
  .push-title {
    padding: 30rpx 30rpx 15rpx;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 24rpx;
    font-weight: 400;
    color: #4f555b;
    .push-type {
      display: flex;
      align-items: center;
      font-size: 24rpx;
      font-weight: 400;
      color: #4f555b;
      image {
        width: 24rpx;
        height: 24rpx;
        margin-right: 10rpx;
      }
    }
  }
  .push-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24rpx 0 30rpx;
    .push-content {
      width: calc(100% - 150rpx);
      .push-content-title {
        font-size: 30rpx;
        font-weight: 500;
        color: #202123;
        margin-bottom: 20rpx;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .push-content-text {
        font-size: 24rpx;
        font-weight: 400;
        color: #4f555b;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }
    .push-img {
      width: 100rpx;
      height: 100rpx;
      background: #f2f2f3;
      border: 1rpx solid #e9e9e9;
      border-radius: 10rpx;
      margin-left: 50rpx;
    }
  }
}
.slide-fade-enter-active {
  transition: all 0.3s ease;
}
.slide-fade-leave-active {
  transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter, .slide-fade-leave-to
    /* .slide-fade-leave-active for below version 2.1.8 */ {
  transform: translateX(20rpx);
  opacity: 0;
}
</style>

app_push.js

export class appPush {
  constructor(option = {}) {
    console.log(option)
    // app内横幅提醒
    this.inApp = option.inApp
    // 声音提醒
    this.voice = option.voice
    // 振动提醒
    this.vibration = option.vibration
    // 消息分类
    this.messageType = option.messageType || ''
    // 通知标题
    this.messageTitle = option.messageTitle || ''
    // 时间
    this.messageTime = option.messageTime || '现在'
    // 通知文案
    this.messageContent = option.messageContent || ''
    // 缩略图
    this.messageImage = option.messageImage || ''
    this.tap = option.tap || (() => {})

    this.screenWidth = plus.screen.resolutionWidth
    this.screenHeight = plus.screen.resolutionHeight
    // 比例
    this.propotation = this.screenWidth / 750
    //弹窗容器宽度
    this.popupViewWidth = this.propotation * 710
    // 弹窗容器高度
    this.popupViewHeight = this.propotation * 192
    // 弹窗容器的Padding
    this.viewContentPadding = this.propotation * 30
    // 弹框容器的宽度
    this.viewContentWidth = parseInt(this.popupViewWidth - this.viewContentPadding * 2)
    // 弹框到顶部的距离
    this.system = uni.getSystemInfoSync()
    // 过度时间
    this.duration = 200
    // 关闭时间
    this.closeTime = 3000

    console.log(this.inApp, this.voice, this.vibration)

    this.top = this.propotation * 20 + this.system.statusBarHeight
    this.initTop = -this.system.statusBarHeight

    this.body = null
    this.bodyBg = null
    this.timer = null

    this.flag = false
  }

  // 生成弹框主体
  createView() {
    let view = new plus.nativeObj.View('popupView', {
      // tag: 'rect',
      top: this.propotation * 20 + this.system.statusBarHeight,
      left: this.propotation * 20,
      height: this.popupViewHeight,
      width: this.popupViewWidth,
    })
    // 绘制白色背景
    view.drawRect({
      color: '#fff',
      radius: '10px',
    })
    let viewContentList = [
      {
        src: '/static/message-icon.png',
        id: 'icon',
        tag: 'img',
        position: {
          top: this.viewContentPadding + 'px',
          left: this.viewContentPadding + 'px',
          width: this.propotation * 24 + 'px',
          height: this.propotation * 24 + 'px',
        },
      },
      {
        tag: 'font',
        id: 'pop-title',
        text: this.messageType,
        textStyles: {
          size: this.propotation * 24 + 'px',
          align: 'left',
          color: '#4F555B',
        },
        position: {
          top: this.viewContentPadding + 'px',
          left: this.propotation * 64 + 'px',
          height: this.propotation * 24 + 'px',
          width: this.viewContentWidth + 'px',
        },
      },
      {
        tag: 'font',
        id: 'time',
        text: this.messageTime,
        textStyles: {
          size: this.propotation * 24 + 'px',
          align: 'right',
          color: '#4F555B',
        },
        position: {
          top: this.viewContentPadding + 'px',
          left: this.viewContentPadding + 'px',
          height: this.propotation * 24 + 'px',
          width: this.viewContentWidth + 'px',
        },
      },
      {
        tag: 'font',
        id: 'push-title',
        text: this.messageTitle,
        textStyles: {
          size: this.propotation * 30 + 'px',
          align: 'left',
          color: '#202123',
          overflow: 'ellipsis',
        },
        position: {
          top: this.propotation * 82 + 'px',
          left: this.propotation * 30 + 'px',
          height: this.propotation * 30 + 'px',
          width: this.propotation * 505 + 'px',
        },
      },
      {
        tag: 'font',
        id: 'push-content',
        text: this.messageContent,
        textStyles: {
          size: this.propotation * 24 + 'px',
          align: 'left',
          color: '#4F555B',
          overflow: 'ellipsis',
        },
        position: {
          top: this.propotation * 130 + 'px',
          left: this.propotation * 30 + 'px',
          height: this.propotation * 24 + 'px',
          width: this.propotation * 505 + 'px',
        },
      },
      {
        src: this.messageImage,
        id: 'image',
        tag: 'img',
        position: {
          top: this.propotation * 68 + 'px',
          // right: "0px",
          left: this.propotation * 586 + 'px',
          width: this.propotation * 100 + 'px',
          height: this.propotation * 100 + 'px',
        },
      },
    ]
    view.draw(viewContentList)
    view.addEventListener('click', () => {
      this.tap()
      this.hide()
    })
    this.body = view
  }
  // 显示/关闭弹框动画
  modelAnimationOpenOrClose(type) {
    var options = { type: type, duration: this.duration }
    plus.nativeObj.View.startAnimation(options, this.body, () => {
      // 关闭原生动画
      plus.nativeObj.View.clearAnimation()
    })
  }
  bgAnimationOpenOrClose(type) {
    var options = { type: type, duration: this.duration }
    plus.nativeObj.View.startAnimation(options, this.bodyBg, () => {
      // 关闭原生动画
      plus.nativeObj.View.clearAnimation()
    })
  }
  // 显示弹框
  show() {
    this.tips()
    if (this.inApp) {
      this.createView()
      this.body.show()
      setTimeout(() => {
        if (this.body) {
          this.hide()
        }
      }, this.closeTime)
    }
  }
  // 关闭弹框
  hide() {
    this.modelAnimationOpenOrClose('slide-out-right')
    this.body.hide()
    setTimeout(() => {
      plus.nativeObj.View.clearAnimation()
      this.body = null
    }, this.duration)
  }
  // 调用系统提示音和振动
  tips() {
    if (this.voice) {
      let main = plus.android.runtimeMainActivity()
      let RingtoneManager = plus.android.importClass('android.media.RingtoneManager')
      let uri = RingtoneManager.getActualDefaultRingtoneUri(main, RingtoneManager.TYPE_NOTIFICATION)
      console.log(uri)
      let MediaPlayer = plus.android.importClass('android.media.MediaPlayer')
      let player = MediaPlayer.create(main, uri)
      player.setLooping(false)
      player.prepare()
      player.start()
    }
    if (this.vibration) {
      plus.device.vibrate()
    }
  }
}

export default appPush

index.js

import app_push from './app_push.js'
import h5Push from './h5_push.vue'

const appPush = {
    install: function(Vue) {
        Vue.prototype.$appPush = function(op = {}) {
            // #ifdef APP-PLUS
            new app_push({
                ...op
            }).show();
            // #endif
            // #ifdef H5
            // 创建构造器
            const H5PushInstance = Vue.extend(h5Push)
            let instance = new H5PushInstance({
                data: op
            })
            instance.$mount()
            document.body.appendChild(instance.$el)
            Vue.nextTick(() => {
                instance.show = true
            })
            // #endif
        }
    }
}

export default appPush

main.js

import appPush from '@/plugins/APPPush/index.js'
Vue.use(appPush)

 使用:

let params = {
        inApp: true, // app内横幅提醒
        voice: true, // 声音提醒
        vibration: true, // 振动提醒
        messageType: typeMap[res.type],
        messageTitle: res.name,
        messageContent: res.content,
        messageImage: "/static/58x58.png",
        tap: () => this.jump(res),
};
this.$appPush(params);