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
}
}