vue自定义插件封装,实现简易的elementUi的Message和MessageBox

vue自定义插件封装示例


1、实现message插件封装(类似简易版的elementUi的message)


message组件

<template>
    <transition name="msgbox-fade" @after-leave="handleAfterLeave">
        <div
            :class="[
                'message_wrapper',
                { success: type === 'success' },
                { warning: type === 'warning' },
                { error: type === 'error' },
                { info: type === 'info' },
            ]"
            v-show="visible"
            :style="{ top: styleTop + 'px' }"
        >
            {{ message }}
        </div>
    </transition>
</template>

<script>
export default {
    name: 'message',
    data() {
        return {
            // 提示消息文本
            message: '',
            // 类型
            type: '',
            // 显示/隐藏
            visible: false,
            // 定位高度
            styleTop: 20,
        }
    },
    methods: {
        /**
         * @description: message显示
         */
        messageShow() {
            this.visible = true
        },



        /**
         * @description: message隐藏
         */
        messageHide() {
            this.visible = false
        },



        /**
         * @description: 销毁组件
         */
        handleAfterLeave() {
            this.$destroy(true)
            this.$el.parentNode.removeChild(this.$el)
        },
    },
}
</script>



<style scoped lang="scss">
.message_wrapper {
    position: fixed;
    min-width: 380px;
    left: 50%;
    z-index: 99999;
    color: #fff;
    padding: 15px 15px 15px 20px;
    font-size: 14px;
    border-radius: 4px;
    top: 20px;
    transform: translateX(-50%);
    background: #fff;
    color: #909399;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
    line-height: 1;
    &.success{
        background: mix(#ffffff, #67C23A, 90%);
        color: #67C23A;
    }
    &.warning{
        background: mix(#ffffff, #E6A23C, 90%);
        color: #E6A23C;
    }
    &.error{
        background: mix(#ffffff, #F56C6C, 90%);
        color: #F56C6C;
    }
    &.info{
        background: mix(#ffffff, #909399, 90%);
        color: #909399;
    }
    i {
        margin-right: 4px;
    }
}
.msgbox-fade-enter-active {
    -webkit-animation: msgbox-fade-in 0.3s;
    animation: msgbox-fade-in 0.3s;
}
.msgbox-fade-leave-active {
    -webkit-animation: msgbox-fade-out 0.3s;
    animation: msgbox-fade-out 0.3s;
}
@keyframes msgbox-fade-in {
    0% {
        transform: translate3d(-50%, -20px, 0);
        opacity: 0;
    }
    100% {
        transform: translate3d(-50%, 0, 0);
        opacity: 1;
    }
}
@keyframes msgbox-fade-out {
    0% {
        transform: translate3d(-50%, 0, 0);
        opacity: 1;
    }
    100% {
        transform: translate3d(-50%, -20px, 0);
        opacity: 0;
    }
}
</style>

对应的message.js文件

import Vue from 'vue'
import messageComponent from './index.vue'


const messageConstructor = Vue.extend(messageComponent)
let instances = []
let seed = 1
function messageFun(obj) {
    let { message, type, duration } = obj
    const messageDom = new messageConstructor({
        el: document.createElement('div'),
        data() {
            return {
                message: message,
                type: type,
            }
        },
    })
    let id = 'my_message_' + seed++
    let styleTop = 20
    document.body.appendChild(messageDom.$el)
    messageDom.id = id
    instances.forEach(item => {
        styleTop += item.$el.offsetHeight + 16
    })
    messageDom.styleTop = styleTop
    messageDom.messageShow()
    instances.push(messageDom)
    // 过了 duration 时间后隐藏
    duration = duration ? duration : 3000
    setTimeout(() => {
        let len = instances.length
        messageDom.messageHide()
        let removedHeight = messageDom.$el.offsetHeight
        let index = instances.findIndex(e => e.id === messageDom.id)
        if (len > 1) {
            for (let i = index; i < len; i++) {
                let dom = instances[i].$el
                dom.style['top'] =
                    parseInt(dom.style['top'], 10) - removedHeight - 16 + 'px'
            }
        }
        instances.splice(index, 1)
    }, duration)
}



function message() {
    window.$message = Vue.prototype.$message = messageFun
}
export default message

然后在main.ts中注册

// 自定义toast插件
import message from '@/components/notice/message/index.js'
vue.use(message)

最后就可以在全局地方使用

this.$message({message:"成功",type:'success'})

类似效果如下


2、实现$confirm插件封装(类似简易版的elementUi的messageBox)

  主要用于操作的二次确定

confirm组件

这里按钮点击事件设置一个callback回调,用于方便后面的操作交互

<template>
    <transition name="confirmbox-fade">
        <div
            class="confirm_wrapper"
            v-show="visible"
            @click="otherClick"
        >
            <div class="confirm_box" ref="confirmBox">
                <p class="confirm_title">
                    <span class="sign"></span>
                    {{ title || "提示" }}
                </p>
                <p class="content_text">
                    {{ message }}
                </p>
                <div class="footer_button">
                    <div class="button" @click="cancel">取消</div>
                    <div class="button define" @click="define">确定</div>
                </div>
            </div>
        </div>
    </transition>
</template>

<script>
export default {
    name: 'Confirm',
    data() {
        return {
            // 标题(默认 提示)
            title: '',
            // 提示文本
            message: '',
            // 显示或隐藏
            visible: false,
        }
    },
    methods: {
        /**
         * @description: 其他地方点击隐藏弹框
         */
        otherClick(e) {
            let confirmBox = this.$refs.confirmBox
            if (e.target.contains(confirmBox)) {
                this.cancel()
            }
        },


        /**
         * @description: 移出
         */
        close() {
            this.visible = false
            setTimeout(() => {
                this.$el.parentNode.removeChild(this.$el)
            }, 300)
        },


        /**
         * @description: 确定
         */
        define() {
            this.close()
            this.callback('confirm')
        },


        /**
         * @description: 取消
         */
        cancel() {
            this.close()
            this.callback('cancel')
        },
    },
}
</script>


<style lang="scss" scoped>
.confirm_wrapper {
    position: fixed;
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 99999;
    .confirm_box {
        width: 360px;
        padding-bottom: 10px;
        vertical-align: middle;
        background-color: #fff;
        border-radius: 4px;
        border: 1px solid #ebeef5;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        text-align: left;
        overflow: hidden;
        backface-visibility: hidden;
        .confirm_title {
            display: flex;
            align-items: center;
            height: 50px;
            line-height: 50px;
            padding-left: 8px;
            font-size: 18px;
            .sign {
                height: 15px;
                width: 4px;
                border-radius: 4px;
                margin-right: 8px;
            }
        }
        .content_text {
            padding: 15px 15px 10px 30px;
            color: #606266;
            font-size: 14px;
        }
        .footer_button {
            display: flex;
            justify-content: flex-end;
            padding-top: 24px;
            padding-right: 24px;
            .button{
                line-height: 1;
                cursor: pointer;
                background: #fff;
                border: 1px solid #dcdfe6;
                color: #606266;
                text-align: center;
                box-sizing: border-box;
                transition: .1s;
                padding: 12px 20px;
                font-size: 14px;
                border-radius: 4px;
                &.define{
                    margin-left: 20px;
                    color: #fff;
                    background-color: #409eff;
                    border-color: #409eff;
                }
            }
        }
    }
}
</style>
<style lang="scss" scoped>
.confirmbox-fade-enter-active {
    -webkit-animation: confirmbox-fade-in 0.3s;
    animation: confirmbox-fade-in 0.3s;
}
.confirmbox-fade-leave-active {
    -webkit-animation: confirmbox-fade-out 0.3s;
    animation: confirmbox-fade-out 0.3s;
}
@keyframes confirmbox-fade-in {
    0% {
        transform: translate3d(0, -20px, 0);
        opacity: 0;
    }
    100% {
        transform: translate3d(0, 0, 0);
        opacity: 1;
    }
}
@keyframes confirmbox-fade-out {
    0% {
        transform: translate3d(0, 0, 0);
        opacity: 1;
    }
    100% {
        transform: translate3d(0, -20px, 0);
        opacity: 0;
    }
}
</style>

对应的index.js文件

  这里使用Promise来为用户点击确定或者取消做对应的交互触发

import Vue from 'vue'
import ConfirmBox from './index.vue'
const ConfirmBoxConstructor = Vue.extend(ConfirmBox)

let instance
const initInstance = (message, title) => {
    instance = new ConfirmBoxConstructor({
        el: document.createElement('div'),
        data() {
            return {
                title,
                message,
            }
        },
    })
}


const showConfirm = obj => {
    let { message, title } = obj
    return new Promise((reslove, reject) => {
        initInstance(message, title)
        instance.callback = action => {
            if (action === 'confirm') {
                reslove()
            } else if (action === 'cancel') {
                reject()
            }
        }
        document.body.appendChild(instance.$el)
        Vue.nextTick(() => {
            instance.visible = true
        })
    })
}


function registryConfirm() {
    Vue.prototype.$confirm = showConfirm
}
export default registryConfirm

接下来在main.js中

// 自定义confirm插件
import messageBox from '@/components/notice/messageBox/index.js'
vue.use(messageBox)

全局使用

 this.$confirm({
    message: '弹窗测试'
 })
   .then(() => {})
   .catch(() => {});

类似效果如下


这时,点击确定按钮就会触发 .then里的事件,点击取消则触发 .catch里的事件


typescript对应的声明文件

   如果插件是用typescript书写的,则以下是对应的声明文件,仅供参考

import Vue from "vue";
declare module "vue/types/vue" {
    interface Vue {
        $message: any,
        $confirm: any
    }
}
posted @ 2020-10-14 00:59  陈山豆  阅读(3296)  评论(2编辑  收藏  举报