vue模态框
modal.vue
<template>
<div>
<div class="dialog-mask" v-show="visible" v-if="maskhide"></div>
<div class="dialog" @click.self="maskClose" v-show="visible">
<div class="dialog-main"
:style="dialogStyle"
:class="{'dialog-fullscreen':fullscreenable}"
>
<div class="dialog-header" v-if="$slots.title || title">
<slot name="title">{{title}}</slot>
<i class="dialog-fullcreen iconfont"
:class="{
'icon-normal':fullscreenable,
'icon-max':!fullscreenable
}"
v-if="fullscreen"
@click="fullscreenable = !fullscreenable"></i>
<i class="dialog-close iconfont icon-close" v-if="headClose" @click="close"></i>
</div>
<div class="dialog-body">
<slot>{{content}}</slot>
</div>
<div class="dialog-footer" v-if="$slots.footer || okText || cancelText">
<slot name="footer">
<button class="dialog-btn" v-if="okText" @click="ok"><span>{{okText}}</span></button>
<button class="dialog-btn" v-if="cancelText" @click="cancel"><span>{{cancelText}}</span></button>
</slot>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "veDialog",
props:{
value:{
type: Boolean,
default: false
},
closeable:{
type: Boolean,
default: false
},
maskhide:{
type: Boolean,
default: true
},
title:{
type: String,
default:""
},
headClose:{
type:Boolean,
default: true
},
okText: String,
cancelText: String,
content:{
type: String,
default: "这是弹框"
},
size:{
type: Number,
default: 520
},
styles:{
type:Object
},
fullscreen:{
type: Boolean,
default: true
}
},
data(){
return{
visible: this.value,
fullscreenable: false
}
},
computed:{
dialogStyle(){
let _style = {};
let widthStyle = {
width: this.size>100?`${this.size}px`:`${this.size}%`
}
let customStyle = this.styles || {};
return Object.assign(_style,widthStyle, customStyle);
}
},
methods:{
close(){
this.visible = false;
this.$emit("input", false);
this.$emit('on-cancel')
},
maskClose(){
this.closeable && this.close()
},
ok(){
this.visible = false;
this.$emit("input", false);
this.$emit('on-ok')
},
cancel(){
this.close();
},
EscClose (e) {
if (this.visible && this.closable) {
if (e.keyCode === 27) {
this.close()
}
}
},
},
mounted(){
document.addEventListener('keydown', this.EscClose);
},
beforeDestroy(){
document.removeEventListener('keydown', this.EscClose);
},
watch:{
value (val){
this.visible = val;
},
visible(val){
this.$emit("on-visible-change", val)
}
}
}
</script>
dialog.js
import Vue from 'vue';
import Dialog from './modal';
Dialog.newInstace = proper => {
const _props = proper || {};
const Instance = new Vue({
data: Object.assign({}, _props, {
visible: false,
size: 416,
title: "",
content: "",
fullscreen: true,
okText: undefined,
cancelText: undefined,
showCancel: false,
clasealbe:false
}),
render(h){
let footerVNode = [];
footerVNode.push(h('button', {
attrs:{
class: ['dialog-btn ok-btn'],
},
on:{
click: this.ok
}
}, this.localeOkText));
if(this.showCancel){
footerVNode.push(h('button', {
attrs:{
class: ['dialog-btn','cancel-btn'],
},
on:{
click: this.cancel
}
}, this.localeCancelText))
}
let header_render;
if(this.title){
header_render = this.iconType?
h('div',{
slot: 'title',
},[
h('i',{
class: this.iconTypeCls
}),
h('span',{
domProps:{
innerHTML: this.title
}
})
])
:h('div',{
slot:'title',
domProps:{
innerHTML: this.title
}
});
}
let body_render;
if(this.render){
body_render = h('div', {},[this.render(h)])
}else{
body_render = h('div', {
domProps: {
innerHTML: this.body
}
})
}
return h(Dialog, {
props: Object.assign({}, _props,{
size: this.size,
closable: this.closable,
fullscreen: this.fullscreen
}),
domProps:{
value: this.visible
},
on:{
input: status => {
this.visible = status;
},
"on-cancel": this.cancel
}
},[
header_render,
body_render,
h('template', {
slot: "footer"
},footerVNode)
])
},
computed:{
localeOkText(){
if(this.okText){
return this.okText
}else{
return "确定"
}
},
localeCancelText(){
if(this.cancelText){
return this.cancelText
}else{
return "取消"
}
},
iconTypeCls(){
return [
'iconfont',
`icon-${this.iconType}`,
'title-icon',
this.iconType
]
}
},
methods:{
cancel(){
this.$children[0].visible = false;
this.onCancel();
this.remove()
},
ok(){
this.$children[0].visible = false;
this.remove();
this.onOk();
},
remove(){
setTimeout(() => {
this.destroy();
}, 300);
},
destroy(){
this.$destroy();
document.body.removeChild(this.$el);
this.onRemove();
},
onOk () {},
onCancel () {},
onRemove () {}
}
});
const component = Instance.$mount();
document.body.appendChild(component.$el);
const dialog = Instance.$children[0];
return {
show(props){
dialog.$parent.showCancel = props.showCancel;
dialog.$parent.iconType = props.icon;
if ('size' in props) {
dialog.$parent.size = props.size;
}
if ('closable' in props) {
dialog.$parent.closable = props.closable;
}
if ('title' in props) {
dialog.$parent.title = props.title;
}
if ('content' in props) {
dialog.$parent.body = props.content;
}
if ('okText' in props) {
dialog.$parent.okText = props.okText;
}
if ('cancelText' in props) {
dialog.$parent.cancelText = props.cancelText;
}
if ('fullscreen' in props) {
dialog.$parent.fullscreen = props.fullscreen;
}
if ('onCancel' in props) {
dialog.$parent.onCancel = props.onCancel;
}
if ('onOk' in props) {
dialog.$parent.onOk = props.onOk;
}
dialog.$parent.onRemove = props.onRemove;
dialog.visible = true;
},
remove(){
dialog.visible = false;
dialog.$parent.remove();
},
component: dialog
}
}
export default Dialog;
index.js
import './dialog.scss';
import Dialog from './dialog';
let dialogInstance;
function getDialogInstance(render=undefined){
dialogInstance = dialogInstance || Dialog.newInstace({
closeable: false,
render: render
});
return dialogInstance;
}
function confirm(opt){
const render = ('render' in opt)?opt.render:undefined;
let instance = getDialogInstance(render);
opt.onRemove = function(){
dialogInstance = null;
}
instance.show(opt);
}
Dialog.info = function(props = {}){
props.icon = 'info';
props.showCancel = false;
props.size = 300;
return confirm(props)
}
Dialog.success = function(props = {}){
props.icon = 'success';
props.showCancel = false;
props.size = 300;
return confirm(props)
}
Dialog.warning = function(props = {}){
props.icon = 'warning';
props.showCancel = false;
props.size = 300;
return confirm(props)
}
Dialog.error = function (props = {}) {
props.icon = 'error';
props.showCancel = false;
props.size = 300;
return confirm(props);
};
Dialog.confirm = function (props = {}) {
props.showCancel = true;
return confirm(props);
};
Dialog.remove = function(){
if(!dialogInstance){
return false;
}
const instance = getDialogInstance();
instance.remove();
}
export default Dialog;
dialog.scss
$prefix :"dialog";
$zIndex :1000;
*{
padding: 0;
margin: 0;
}
:root{
font-size: 12px;
}
.#{$prefix}-mask{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(#000000, .2);
z-index: $zIndex;
}
.#{$prefix}{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: $zIndex + 1;
.#{$prefix}-main{
width: 300px;
background-color: #fff;
position: absolute;
top: 20%;
left: 50%;
transform: translate(-50%, -20%);
border-radius: 5px;
box-shadow: 0 0 20px #999;
display: flex;
flex-direction: column;
max-height: 80%;
&.dialog-fullscreen{
top: 0;
left: 0;
width: 100vw !important;
height: 100vh !important;
transform: none;
// max-width: 100%;
max-height: 100%;
}
.#{$prefix}-header{
line-height: 3rem;
border-bottom: 1px solid #eee;
box-shadow: 0px 0px 10px #999;
padding: 0 1rem;
.title-icon{
margin-right: .5rem;
&.info{
color: #2196f3;
}
&.success{
color: #4caf50;
}
&.warning{
color: #ff9800;
}
&.error{
color: #f44336;
}
}
span{
font-weight: 900;
}
.#{$prefix}-fullcreen{
position: absolute;
top: 0;
right: 3rem;
cursor: pointer;
color: #666;
&:hover{
color: #333;
}
}
.#{$prefix}-close{
position: absolute;
top: 0;
right: 1rem;
cursor: pointer;
color: #666;
&:hover{
color: #333;
}
}
}
.#{$prefix}-body{
flex: 1;
overflow: auto;
}
.#{$prefix}-footer{
padding: 1rem 0;
border-top: 1px solid #eee;
box-shadow: 0px -1px 10px #999;
padding: 0 1rem;
.#{$prefix}-btn{
float: right;
margin: .5rem 0 .5rem 1rem;
background-color: #fff;
border: none;
line-height: 2rem;
padding: 0 .5rem;
border: 1px solid #eee;
border-radius: 5px;
cursor: pointer;
&:hover{
background: #2196f3;
color: #fff;
border-color: #2196f3;
opacity: .75;
}
// &:focus{
// }
// outline: none;
}
}
}
}