Message 通知

2021-02-04  本文已影响0人  skoll

使用

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
import router from '../router/index'
import store from './vuex'
import Message from '../src/components/message/index'
import Notice from "../src/components/notice/index"
Vue.prototype.$Message=Message
Vue.prototype.$Notice=Notice

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  router,
  store,
})

代码

import Notification from '../base/index'
let prefixCls='li-message-notification'
const iconPrefixCls = 'li-icon';
const prefixKey = 'li-message-key-';

const iconTypes = {
    'info': 'ios-information-circle',
    'success': 'ios-checkmark-circle',
    'warning': 'ios-alert',
    'error': 'ios-close-circle',
    'loading': 'ios-loading'
};


const defaults = {
    top: 24,
    duration: 1.5
}

let messageInstance;
function getMessageInstance () {
    messageInstance = messageInstance || Notification.newInstance({
        prefixCls: prefixCls,
        styles: {
            top: `${defaults.top}px`
        },
        pre:'li-message'
    });

    return messageInstance;
}

function notice (content = '', duration = defaults.duration, type, onClose = function () {}, closable = false,colorStyles) {
    const iconType = iconTypes[type];

    // if loading
    const loadCls = type === 'loading' ? ' ivu-load-loop' : '';

    let instance = getMessageInstance();
    instance.notice({
        name: `${prefixKey}${name}`,
        duration: duration,
        styles: {},
        // transitionName: 'move-up',
        // 这里专门加了message进入和离开需要的渐变特效
        content: content,
        onClose: onClose,
        closable: closable,
        type: 'message',
        msgType: type,
        prefixCls:'li-message',
        colorStyles:colorStyles,
        // 这里要把里面的样式参数传进去
        // 这里也进行了参数赋值
    });

    // 用于手动消除
    return (function () {
        let target = name++;

        return function () {
            instance.remove(`${prefixKey}${target}`);
        };
    })();
}


export default{
    name:"li-message",
    components:{Notification},
    info(options){
        return this.message('info',options)
    },
    success(options){
        return this.message('success',options)
    },
    warning(options){
        return this.message('warning',options)
    },
    error(options){
        return this.message('error',options)
    },
    loading(options){
        return this.message("loading",options);
    },
    message(type,options){
          if(typeof options=='string'){
            //证明用户只是挑简单输入了一个提示语句,别的都使用默认设置
            options={
                content:options
            }
          }
          return notice(options.content,options.duration,type,options.onClose,options.closable,options.colorStyles)
    },
    config(options){
        // 自定义设置
        if(options.top||options.top===0){
            defaults.top=option.top
        }

        if(options.duration||options.duration===0){
            defaults.duration=options.duration
        }
    },
    destory(){
        let instance=getMessageInstance()
        messageInstance=null
        instance.destory('li-message')
    }
}


index.js

// 还是需要一个index.js
import Vue from 'Vue'
import Notification from './notification.vue';
// 这里必须是实例化出来,在操作方法,不能在别的地方直接import,然后绕着那个方法,除非那个数据也是全局的。

// 现在这里面实例化一个组件
Notification.newInstance = properties => {
    const _props = properties || {};

    const Instance = new Vue({
        render (h) {
            return h(Notification, {
                props: _props
            });
        }
    });

    const component = Instance.$mount();
    document.body.appendChild(component.$el);
    const notification = Instance.$children[0];

    return {
        // 通过这些暴露的方法来操作内部的数据
        // 如果用最最一开始的方法,无法读取到里面的this
        notice (noticeProps) {
            notification.add(noticeProps);
        },
        remove (name) {
            notification.close(name);
        },
        component: notification,
        destroy (element) {
            notification.closeAll();
            setTimeout(function() {
                document.body.removeChild(document.getElementsByClassName(element)[0]);
            }, 500);
        }
    };
};

export default Notification;

index.less

···
@import '../../assets/gLess.less';

@notice:.li-notice-notification;
@message:.li-message-notification;

// notice外层包裹
@{message}{
font-size: @font-size-base;
position: fixed;
z-index:@zindex-message;
width:100%;
}
@{notice}{
//这里没有使用width:100%。一来是没必要,二来是如果宽度100%,
// 而且还是最上层覆盖,可能会导致想要点击的东西点击不到。
font-size: @font-size-base;
position: fixed;
z-index:@zindex-message;
width:335px;
right:0px;
}

// message外层包裹

// 过渡样式
// message过渡样式
// transition-group样式

.li-message-enter{
opacity: 0;
transform: translateY(-50px);
animation-timing-function: @ease-in-out;
}
.li-message-leave-to{
opacity: 0;
transform: translate(-30px,-50px);
}
.li-message-leave-active {
position: absolute;
animation-timing-function: @ease-in-out;
// 为什么到最后一个的时候,离开的时候加上这个样式就会导致原来的样式崩塌
// 但是这个和示范的样式是一模一样的啊
// 这真是见了鬼了。
}

.li-notice-enter{
opacity: 0;
transform:translateX(100%);
}
.li-notice-leave-to{
opacity: 0;
transform:translateX(100%)
}
.li-notice-leave-active{
position: absolute;
}

@keyframes ivuMoveDownIn {
0% {
transform-origin: 0 0;
transform: translateY(100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
}

@keyframes ivuMoveDownOut {
0% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateY(100%);
opacity: 0;
}
}

@keyframes ivuMoveLeftIn {
0% {
transform-origin: 0 0;
transform: translateX(-100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
}

@keyframes ivuMoveLeftOut {
0% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateX(-100%);
opacity: 0;
}
}

@keyframes ivuMoveRightIn {
0% {
opacity: 0;
transform-origin: 0 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform-origin: 0 0;
transform: translateX(0%);
}
}

@keyframes ivuMoveRightOut {
0% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateX(100%);
opacity: 0;
}
}

@keyframes ivuMoveUpIn {
0% {
transform-origin: 0 0;
transform: translateY(-100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
}

// 可以发现最明显的例子就是这个里面的移动数据都是百分比,跟我的直接填具体的数值不同
@keyframes ivuMoveUpOut {
0% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateY(-100%);
opacity: 0;
}
}
@keyframes ivuMoveNoticeIn {
0% {
opacity: 0;
transform-origin: 0 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform-origin: 0 0;
transform: translateX(0%);
}
}

@keyframes ivuMoveNoticeOut {
0% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
70% {
transform-origin: 0 0;
transform: translateX(100%);
height: auto;
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateX(100%);
height: 0;
padding: 0;
margin-bottom: 0;
opacity: 0;
}
}
···

Message.less

@import '../../assets/gLess.less';

@name:.li-message;

// 最外层的包裹
.li-message-wrapper{
    display: flex;
    width: 100%;
    transition: all 0.5s;
    justify-content: center;
}

@{name}{
    margin-top:15px;
    padding: 8px 8px;
    
    display:flex;

    background-color: #fff;
    border-radius: @border-radius-small;
    box-shadow: @shadow-base;
    border:1px solid #fff;

    &-close{
        color:#999;
    }

    &-content{
       padding-left:10px;
       padding-right:10px;
    }

    &-icon{
        
    }

    &-success &-icon{
        color:@success-color;
    }

    &-error &-icon{
        color:@error-color;
    }

    &-warning &-icon{
        color:@warning-color;
    }

    &-info &-icon{
        color:@primary-color;
    }
}

notice.less

@import '../../assets/gLess.less';

// notice样式
@notice:.li-notice;
@notice-width:335px;
@notice-padding:16px;
@notice-margin-bottom:10px;

@{notice}{
    // 这个左右应该是有宽度大小的,可以上下扩展
    width:85%;
    margin-bottom:@notice-margin-bottom;
    padding: 8px 8px;
    
    display:flex;

    background-color: #fff;
    border-radius: @border-radius-small;
    box-shadow: @shadow-base;
    border:1px solid #fff;

    flex-direction: column;


    &-wrapper{
        display: flex;
        width:@notice-width;
        transition: all 0.5s;
        justify-content: center;
        // position: relative;
        // left:-20px;
        // 加上position属性,消失的时候没有使用希望的样式,所以使用transform来移动位置巴
        // 子元素里面应该可以加,里面也是不行的
    }

    &-title{
        height:30px;
        display: flex;
        flex-direction: row;
        font-size: @font-size-large;
        color:@title-color;    
    }

    &-icon{
        width: 30px;
    }

    &-h{
        flex:1;
        line-height: 30px;
        overflow: hidden;
        text-emphasis:ellipsis;
        white-space: nowrap;
    }

    &-close{
        width:30px;
    }

    &-content{
        flex:1;
        min-height:50px;
        font-size: @font-size-base;
        color:@text-color;
        text-align: justify;
        line-height: 1.5;
    }

}

// notification样式

Notice.vue

<template>
<!-- 首先所有这种弹出框架都是用的这个基础类型,有共同的父容器。 -->
<div :class="[`${prefixCls}-wrapper`]" @click="close">
        <div :class="classes" :style="computedStyles">
            <template v-if="type=='notice'">
                    <div class="li-notice-title">
                        <div class="li-notice-icon" v-if="withIcon">
                            X
                        </div>
                        <div class="li-notice-h">
                            {{title}}
                        </div>
                        <div class="li-notice-close" v-if="closable">
                            X
                        </div>
                    </div>
                    <div class="li-notice-content" v-if="content">
                        {{content}}
                    </div>
            </template>
            <template v-if="type=='message'">
                    <div 
                        v-if="true"
                        class="li-message-icon"
                        :style="iconStyles"
                        >
                            * 
                    </div>
                    <div class="li-message-content" @click="close">
                        {{content}}
                    </div>
                    <div 
                        @click="close"
                        v-if="true"
                        class="li-message-close">
                            X
                    </div>   
                    <!-- 这里还可以直接懒加载一个组件进去,这些就都不用操作了 -->
            </template>
        </div>
</div> 
</template>
<script>
export default {
    // 在这里也有自定义设置
    props:{
        prefixCls:{
            type:String,
            default:''
        },
        duration:{
            type:Number,
            default:1.5,
        },
        type:{
            type:String,
        },
        content:{
            type:String,
            default:''
        },
        withIcon:Boolean,
        render:{
            type:Function
        },
        // 这里原本是可以传入一个自定义函数的,但是我们可以传入一个组件的路径,这里显示一个组件
        // 功能既全,而且也很简单
        conponentSec:{
            type:String,
            default:"",
        },
        title:{
            type:String,
            default:"这是一个没有给名字的标题"
        },
        styles:{
            type:Object,
            default:function(){
                return {
                    right:'50%'
                }
            }
        },
        closable:{
            type:Boolean,
            default:false,
        },
        className:{
            type:String,
        },
        name:{
            type:String,
            required:true,
        },
        onClose:{
            type:Function,
        },
        transitionName:{
            type:String,
            default:'test'
        },
        msgType:{
            type:String,
        },
        isShow:{
            type:Boolean,
            default:true,
            // required:true,
        },
        colorStyles:{
            type:Object,
            default:function(){
                return {}
            }
        },
        // content:{
        //     type:String,
        //     default:'这是一个没有内容的提醒'
        // }
        // 这里重复定义的话,会覆盖,并且使用后面的值
    },
    data(){
        return {
            withDesc:false,
        }
    },
    methods:{
        clearCloseTimer(){
            if(this.closeTimer){
                clearTimeout(this.closeTimer)
                this.closeTimer=null;
            }
        },
        close(){
            // this.clearCloseTimer()
            console.log('close')
            this.onClose()
            this.$parent.$parent.close(this.name)
            // 第一个parent取到的只是transition-group组件
        },
    },
    computed:{
        // class,style一定要写在computed里面
        computedStyles(){
            let styles={}
            styles['background-color']=this.colorStyles['backgroundColor']
            styles['border-color']=this.colorStyles['borderColor']
            styles['color']=this.colorStyles['color']
            return styles
        },
        classes(){
            return [
                `${this.prefixCls}`,
                {
                    [`${this.className}`]:!!this.className,
                }
            ]
        },
        iconStyles(){
            let styles={}
            if(this.colorStyles['iconColor']){
                styles['color']=this.colorStyles['iconColor']
            }
            return styles
        }
    },
    mounted(){
        this.clearCloseTimer()
        console.log(this.prefixCls)
        // 先清除之前可能有的计时器
        // if(this.duration!==0){
        //     this.closeTimer=setTimeout(()=>{
        //         this.close()
        //     },this.duration*1000)
        // }
    }
}
</script>
<style lang="less" src="./message.less"></style>
<style lang="less" src="./notice.less"></style>

notication.vue

<template>
        <transition-group
            :name="pre"
            tag="div"
            :class="classes" :style="wrapStyles"
        >
            <Notice 
                v-for="notice in notices"
                :key="notice.name"
                :prefix-cls="notice.prefixCls"
                :styles="notice.styles"
                :type="notice.type"
                :content="notice.content"
                :title="notice.title"
                :withIcon="notice.withIcon"
                :closable="notice.closable"
                :name="notice.name"
                :background="notice.background"
                :msg-type="notice.msgType"
                :on-close="notice.onClose"
                :colorStyles="notice.colorStyles"
                :duration="notice.duration"
                >
            </Notice>
        </transition-group>
        <!-- <div>
            {{computedName}}-{{classes}}
        </div> -->
</template>
<script>
import Notice from './notice'

let seed=0;
const now=Date.now();
function getUuid(){
    return 'li-notification'+now+'-'+(seed++)
}

export default {
    components:{
        Notice
    },
    props:{
        className:{
            type:String,
        },
        prefixCls: {
                type: String,
                default: ""
        },
        pre:{
            type:String,
            default:''
        },
        content: {
                type: String
            },
    },
    data(){
        return {
            notices:[],
            tIndex:this.handleGetIndex(),
            // 测试数据
            items: [1,2,3,4,5,6,7,8,9],
            nextNum:10,
        }
        
    },
    computed:{
        classes(){
            return [
                `${this.prefixCls}`,
                {[`${this.className}`]:!!this.className}
            ]
        },
        wrapStyles(){
            let styles=Object.assign({},this.styles)
            styles['z-index']=1010+this.tIndex

            return styles
        },
        computedName(){
            // 第一时间可能取不到这个值。这里要一定判断,因为我是取的下面的值
            // 如果取不到会直接报错的,但是上面的取不到会再次尝试,不会出现报错

            // if(this.notices[0]){
            //     return this.notices[0]['prefixCls']
            // }
            // 并不能用这个最后一个会有异常
            // 还是直接加一个新的props参数吧
            // 其实应该把第一个参数减一下,新的参数加起来,但是已经有了很多代码了.

        }
    },
    methods:{
        add(notice){
            const name=notice.name||getUuid()
            const tag=notice.tag||name
            // 看传入的数据是否有tag,如果有tag并且和现在已经显示的tag有重合,则不在显示
            this.notices.map((e)=>{
                if(e.tag===tag){
                    console.log('已经有了,不在显示相同的数据了')
                    return
                }
            })

            let _notice=Object.assign({
                styles:{

                },
                content:'',
                duration:1.5,
                closable:false,
                name:name,
                tag:tag
                // 这里全部设置的默认值
            },notice);
            this.notices.push(_notice)
        },
        close(name){
            const notices=this.notices;

            for(let i=0;i<notices.length;i++){
                if(notices[i].name===name){
                    this.notices.splice(i,1)
                    break;
                }
            }
        },
        closeAll(){
            this.notices=[]
        },
        handleGetIndex(){
            // 这个函数不知道是干啥的
            return this.seed++
        },
    },
    mounted(){
        
    }
}
</script>

<style lang="less" src="./index.less">

</style>
上一篇 下一篇

猜你喜欢

热点阅读